Skip to content

Commit 19a48d1

Browse files
committed
WIP transpilers
Signed-off-by: George Lemon <georgelemon@protonmail.com>
1 parent 2c5799c commit 19a48d1

File tree

5 files changed

+147
-57
lines changed

5 files changed

+147
-57
lines changed

src/tim/app/build.nim

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@
66

77
import std/[os, monotimes, times, strutils, options, ropes]
88

9-
import pkg/[flatty, jsony]
9+
import pkg/flatty
1010
import pkg/kapsis/[cli, runtime]
1111

1212
import ../engine/[ast, parser, codegen, chunk, vm, sym]
13-
import ../engine/stdlib/[libsystem, libtimes, libstrings]
13+
import ../engine/stdlib/[libsystem]
1414

15-
import ../engine/transpilers/javascript/jscodegen
15+
import ../engine/transpilers/[jsgen, pygen, rbgen, phpgen]
1616

1717
proc srcCommand*(v: Values) =
1818
## Transpiles `timl` code to a target source
1919
# parse the script
2020
let
2121
srcPath = getCurrentDir() / $(v.get("timl").getPath)
22-
ext = v.get("-t").getStr
22+
ext = v.get("--ext").getStr
2323
pretty = v.has("--pretty")
2424
flagNoCache = v.has("--nocache")
2525
flagRecache = v.has("--recache")
@@ -79,8 +79,17 @@ proc srcCommand*(v: Values) =
7979
echo e.msg
8080
quit(1)
8181
elif ext == "js":
82-
var jst = jscodegen.initCodeGen(script, module, mainChunk)
82+
var jst = jsgen.initCodeGen(script, module, mainChunk)
8383
echo jst.genScript(program, none(string), isMainScript = true)
84+
elif ext == "py":
85+
var pyt = pygen.initCodeGen(script, module, mainChunk)
86+
echo pyt.genScript(program, none(string), isMainScript = true)
87+
elif ext == "rb":
88+
var rbt = rbgen.initCodeGen(script, module, mainChunk)
89+
echo rbt.genScript(program, none(string), isMainScript = true)
90+
elif ext == "php":
91+
var phpt = phpgen.initCodeGen(script, module, mainChunk)
92+
echo phpt.genScript(program, none(string), isMainScript = true)
8493

8594
#
8695
# AST
Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ proc writeVar(node: Node) {.codegen.} =
122122
result.add(jsAssign % [decl[0].ident, decl[^1].getImplValue])
123123

124124
# Render a node as a Ruby expression for HTML/text output
125-
proc renderHandle(node: Node): string =
125+
proc renderHandle(node: Node, unquoted = true): string =
126126
# Render a node as a JS expression for HTML/text output
127127
case node.kind
128128
of nkString:
@@ -180,27 +180,54 @@ proc writeHtml(node: Node, indent: int = 0) {.codegen.} =
180180
else:
181181
discard
182182
of htmlAttr:
183-
assert attr.attrNode.kind == nkInfix, "attribute node must be an infix. Got " & $(attr.attrNode.kind)
184-
let key = attr.attrNode[1].renderHandle
185-
let value = attr.attrNode[2].renderHandle
186-
customAttrs.add((key, value))
183+
if attr.attrNode.kind == nkInfix:
184+
if attr.attrNode[2].kind == nkInfix:
185+
if attr.attrNode[2][0].ident == "&":
186+
let left = attr.attrNode[2][1]
187+
let right = attr.attrNode[2][2]
188+
let leftVal =
189+
if left.kind == nkString:
190+
left.stringVal
191+
else:
192+
"${" & left.renderHandle(false) & ".toString()}"
193+
let rightVal =
194+
if right.kind == nkIdent and right.ident.len > 0 and right.ident[0] == '$':
195+
"${" & right.ident[1..^1] & "}"
196+
else:
197+
(if right.kind == nkString: right.stringVal
198+
else: "${" & right.renderHandle(false) & ".toString()}")
199+
customAttrs.add((attr.attrNode[1].renderHandle(true), leftVal & rightVal))
200+
else:
201+
customAttrs.add((attr.attrNode[1].renderHandle, attr.attrNode[2].renderHandle))
202+
elif attr.attrNode.kind == nkString:
203+
customAttrs.add((attr.attrNode.stringVal, ""))
187204
else:
188205
discard
189206
let ind = repeatStr(" ", indent)
190207
result.add(ind & "{\n")
191208
result.add(ind & " html += `<" & tag)
209+
192210
if classNames.len > 0:
193-
result.add(" class=\"" & classNames.join(" ") & "\"")
211+
# Join class names with spaces
212+
result.add(" class=\\\"" & classNames.join(" ") & "\\\"")
213+
194214
if idVal.len > 0:
195-
result.add(" id=\"" & idVal & "\"")
215+
# Add id attribute if present
216+
result.add(" id=\\\"" & idVal & "\\\"")
217+
196218
for (key, value) in customAttrs:
197-
result.add(" " & key & "=\"" & value & "\"")
219+
# Add custom attributes
220+
if value.len > 0:
221+
result.add(" " & key & "=\\\"" & value & "\\\"")
222+
else:
223+
result.add(" " & key)
198224
result.add(">`;\n")
199225
for child in node.childElements:
200226
case child.kind
201-
of nkBool, nkInt, nkFloat, nkString:
202-
# directly render primitive values
203-
result.add(ind & " html += `" & child.getImplValue(false) & "`;\n")
227+
of nkBool, nkInt, nkFloat:
228+
result.add(ind & " html += `" & child.renderHandle & "`;\n")
229+
of nkString:
230+
result.add(ind & " html += `" & child.renderHandle(true) & "`;\n")
204231
of nkCall:
205232
# handle function calls
206233
if child[0].ident[0] == '@':
@@ -209,7 +236,8 @@ proc writeHtml(node: Node, indent: int = 0) {.codegen.} =
209236
result.add(gen.genStmt(child, indent + 2))
210237
else:
211238
result.add(gen.genStmt(child, indent + 2))
212-
result.add(ind & " html += `</" & tag & ">`;\n")
239+
if node.tag notin voidHtmlElements:
240+
result.add(ind & " html += `</" & tag & ">`;\n")
213241
result.add(ind & "}\n")
214242

215243
proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =

src/tim/engine/transpilers/phpgen.nim

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ proc getImplValue(node: Node, unquoted = true): string =
9191
of nkInt: $node.intVal
9292
of nkFloat: $node.floatVal
9393
of nkString:
94-
if unquoted: "'" & node.stringVal & "'"
94+
if unquoted: "\"" & node.stringVal & "\""
9595
else: node.stringVal
9696
of nkBool:
9797
$node.boolVal
@@ -112,11 +112,12 @@ proc getImplValue(node: Node, unquoted = true): string =
112112

113113
proc writeVar(node: Node) {.codegen.} =
114114
# Write variable declaration (PHP $var)
115-
case node.kind
116-
of nkVar, nkLet:
117-
discard # PHP uses $ for all variables
118-
else:
119-
result.add(phpConst & " ")
115+
# var prefix: string
116+
# case node.kind
117+
# of nkVar, nkLet:
118+
# prefix = phpVar
119+
# else:
120+
# result.add(phpConst & " ")
120121
for decl in node:
121122
result.add(phpVar & decl[0].ident & " = " & decl[^1].getImplValue & ";\n")
122123

@@ -132,7 +133,11 @@ proc renderHandle(node: Node): string =
132133
of nkBool:
133134
$node.boolVal
134135
of nkIdent:
135-
"$" & node.ident
136+
if node.ident notin ["==", "!=", ">", "<", ">=", "<="]:
137+
phpVar & node.ident
138+
else:
139+
# For operators, just return the ident
140+
node.ident
136141
of nkPrefix:
137142
node[0].renderHandle & node[1].renderHandle
138143
of nkPostfix:
@@ -182,14 +187,14 @@ proc writeHtml(node: Node, indent: int = 0) {.codegen.} =
182187
discard
183188
let ind = repeatStr(" ", indent)
184189
result.add(ind & "{\n")
185-
result.add(ind & " $html .= '<" & tag)
190+
result.add(ind & " $html .= \"<" & tag)
186191
if classNames.len > 0:
187-
result.add(" class=\"" & classNames.join(" ") & "\"")
192+
result.add(" class=\\\"" & classNames.join(" ") & "\\\"")
188193
if idVal.len > 0:
189-
result.add(" id=\"" & idVal & "\"")
194+
result.add(" id=\\\"" & idVal & "\\\"")
190195
for (key, value) in customAttrs:
191-
result.add(" " & key & "=\"" & value & "\"")
192-
result.add(">';\n")
196+
result.add(" " & key & "=\\\"" & value & "\\\"")
197+
result.add(">\";\n")
193198
for child in node.childElements:
194199
case child.kind
195200
of nkBool, nkInt, nkFloat, nkString:
@@ -199,9 +204,13 @@ proc writeHtml(node: Node, indent: int = 0) {.codegen.} =
199204
discard
200205
else:
201206
result.add(gen.genStmt(child, indent + 2))
207+
of nkInfix:
208+
# Handle infix expressions
209+
result.add(ind & " $html .= " & child.renderHandle() & ";\n")
202210
else:
203211
result.add(gen.genStmt(child, indent + 2))
204-
result.add(ind & " $html .= '</" & tag & ">';\n")
212+
if node.tag notin voidHtmlElements:
213+
result.add(ind & " $html .= \"</" & tag & ">\";\n")
205214
result.add(ind & "}\n")
206215

207216
proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
@@ -245,7 +254,7 @@ proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
245254
result.add(ind & "function " & fnName & "(")
246255
result.add(params[1..^1].mapIt("$" & it[0].render).join(", "))
247256
result.add(") {\n")
248-
result.add(ind & " $html = '';\n")
257+
result.add(ind & " $html = \";\n")
249258
result.add(gen.genStmt(node[3], indent + 1))
250259
result.add(ind & " return $html;\n")
251260
result.add(ind & "}\n")
@@ -275,7 +284,7 @@ proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
275284
result.add(gen.genStmt(node[2], indent + 1))
276285
result.add(ind & "}\n")
277286
else:
278-
result.add(ind & "foreach (" & iterable.render & " as $" & varName & ") {\n")
287+
result.add(ind & "foreach (" & iterable.renderHandle & " as $" & varName & ") {\n")
279288
result.add(gen.genStmt(node[2], indent + 1))
280289
result.add(ind & "}\n")
281290
of nkCall:
Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,18 @@ proc writeVar(node: Node, indent: int = 0): string {.codegen.} =
114114
let value = decl[^1].getImplValue
115115
result.add(ind & varName & " = " & value & " # type: " & varType & "\n")
116116

117-
proc renderHandle(node: Node): string =
117+
proc renderHandle(node: Node, unquoted = true): string =
118118
# Render a node as a Python expression for HTML/text output
119119
case node.kind
120120
of nkString:
121-
node.stringVal
121+
if not unquoted: "\"" & node.stringVal & "\""
122+
else: node.stringVal
122123
of nkInt:
123124
$node.intVal
124125
of nkFloat:
125126
$node.floatVal
126127
of nkBool:
127-
if node.boolVal: "True" else: "False"
128+
capitalizeAscii($node.boolVal)
128129
of nkIdent:
129130
node.ident
130131
of nkPrefix:
@@ -164,10 +165,28 @@ proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
164165
else:
165166
discard
166167
of htmlAttr:
167-
assert attr.attrNode.kind == nkInfix, "attribute node must be an infix. Got " & $(attr.attrNode.kind)
168-
let key = attr.attrNode[1].renderHandle
169-
let value = attr.attrNode[2].renderHandle
170-
customAttrs.add((key, value))
168+
if attr.attrNode.kind == nkInfix:
169+
if attr.attrNode[2].kind == nkInfix:
170+
if attr.attrNode[2][0].ident == "&":
171+
# Tim string concatenation for attribute value
172+
let left = attr.attrNode[2][1]
173+
let right = attr.attrNode[2][2]
174+
var leftPy =
175+
if left.kind == nkString:
176+
left.stringVal & "'"
177+
else:
178+
"' + str(" & left.renderHandle(false) & ")"
179+
var rightPy =
180+
if right.kind == nkIdent and right.ident.len > 0 and right.ident[0] == '$':
181+
right.ident[1..^1] & " + '"
182+
else:
183+
(if right.kind == nkString: "'" & right.stringVal
184+
else: "str(" & right.renderHandle(false) & ") + '")
185+
customAttrs.add((attr.attrNode[1].renderHandle(true), leftPy & " + " & rightPy))
186+
else:
187+
customAttrs.add((attr.attrNode[1].renderHandle, attr.attrNode[2].renderHandle))
188+
elif attr.attrNode.kind == nkString:
189+
customAttrs.add((attr.attrNode.stringVal, ""))
171190
else:
172191
discard
173192
let ind = repeatStr(" ", indent)
@@ -177,12 +196,20 @@ proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
177196
if idVal.len > 0:
178197
result.add(" id=\"" & idVal & "\"")
179198
for (name, value) in customAttrs:
180-
result.add(" " & name & "=\"" & value & "\"")
199+
if value.len > 0:
200+
# If value is present, add it as an attribute
201+
result.add(" " & name & "=\"" & value & "\"")
202+
else:
203+
# If no value, just add the attribute
204+
# This is common for boolean attributes like `checked`, `disabled`, etc.
205+
result.add(" " & name)
181206
result.add(">'\n")
182207
for child in node.childElements:
183208
case child.kind
184-
of nkString, nkInt, nkFloat, nkBool:
209+
of nkInt, nkFloat, nkBool:
185210
result.add(ind & "html += '" & child.renderHandle & "'\n")
211+
of nkString:
212+
result.add(ind & "html += '" & child.renderHandle(true) & "'\n")
186213
of nkCall:
187214
# handle function calls
188215
if child[0].ident[0] == '@':
@@ -191,7 +218,8 @@ proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
191218
result.add(gen.genStmt(child, indent))
192219
else:
193220
result.add(gen.genStmt(child, indent))
194-
result.add(ind & "html += '</" & tag & ">'\n")
221+
if node.tag notin voidHtmlElements:
222+
result.add(ind & "html += '</" & tag & ">'\n")
195223

196224
proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
197225
result = Rope()
Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ proc writeVar(node: Node, indent: int = 0): string {.codegen.} =
117117
let value = decl[^1].getImplValue
118118
result.add(ind & varName & " = " & value & "\n")
119119

120-
proc renderHandle(node: Node): string =
120+
proc renderHandle(node: Node, unquoted = true): string =
121121
# Render a node as a Ruby expression for HTML/text output
122122
case node.kind
123123
of nkString:
124-
node.stringVal
124+
if not unquoted: "\"" & node.stringVal & "\""
125+
else: node.stringVal
125126
of nkInt:
126127
$node.intVal
127128
of nkFloat:
@@ -141,7 +142,6 @@ proc renderHandle(node: Node): string =
141142
else:
142143
""
143144

144-
145145
proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
146146
# Write HTML as string concatenation
147147
let tag = node.getTag()
@@ -169,25 +169,40 @@ proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
169169
else:
170170
discard
171171
of htmlAttr:
172-
assert attr.attrNode.kind == nkInfix, "attribute node must be an infix. Got " & $(attr.attrNode.kind)
173-
let key = attr.attrNode[1].renderHandle
174-
let value = attr.attrNode[2].renderHandle
175-
customAttrs.add((key, value))
172+
if attr.attrNode.kind == nkInfix:
173+
let key = attr.attrNode[1].renderHandle
174+
let value =
175+
case attr.attrNode[2].kind
176+
of nkIdent, nkCall:
177+
"#{" & attr.attrNode[2].renderHandle & "}"
178+
else:
179+
attr.attrNode[2].renderHandle
180+
customAttrs.add((key, value))
181+
elif attr.attrNode.kind == nkString:
182+
customAttrs.add((attr.attrNode.stringVal, ""))
176183
else:
177184
discard
178185
let ind = repeatStr(" ", indent)
179-
result.add(ind & "html += '<" & tag)
186+
result.add(ind & "html += \"<" & tag)
180187
if classNames.len > 0:
181-
result.add(" class=\"" & classNames.join(" ") & "\"")
188+
result.add(" class=\\\"" & classNames.join(" ") & "\\\"")
182189
if idVal.len > 0:
183-
result.add(" id=\"" & idVal & "\"")
190+
result.add(" id=\\\"" & idVal & "\\\"")
184191
for (name, value) in customAttrs:
185-
result.add(" " & name & "=\"" & value & "\"")
186-
result.add(">'\n")
192+
if value.len > 0:
193+
# For attributes with values, use name="value"
194+
result.add(" " & name & "=\\\"" & value & "\\\"")
195+
else:
196+
# For attributes without values, just use the name
197+
# e.g. <input disabled>
198+
result.add(" " & name)
199+
result.add(">\"\n")
187200
for child in node.childElements:
188201
case child.kind
189-
of nkBool, nkInt, nkFloat, nkString:
190-
result.add(ind & "html += '" & child.renderHandle & "'\n")
202+
of nkBool, nkInt, nkFloat:
203+
result.add(ind & "html += " & child.renderHandle & "\n")
204+
of nkString:
205+
result.add(ind & "html += " & child.renderHandle(false) & "\n")
191206
of nkCall:
192207
# handle function calls
193208
if child[0].ident[0] == '@':
@@ -196,7 +211,8 @@ proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
196211
result.add(gen.genStmt(child, indent + 2))
197212
else:
198213
result.add(gen.genStmt(child, indent + 2))
199-
result.add(ind & "html += '</" & tag & ">'\n")
214+
if node.tag notin voidHtmlElements:
215+
result.add(ind & "html += \"</" & tag & ">\"\n")
200216

201217
proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
202218
# Generate Ruby code for a statement node

0 commit comments

Comments
 (0)