Posted to tcl by Stu at Tue Jun 04 14:31:43 GMT 2024view raw

  1. #! /usr/bin/env python
  2.  
  3. """pTycl - A variation on Python syntax"""
  4.  
  5. __cow__ = """Copyright (c) 2024 Stuart Cassoff <stwo@users.sourceforge.net>
  6.  
  7. Permission to use, copy, modify, and distribute this software for any
  8. purpose with or without fee is hereby granted, provided that the above
  9. copyright notice and this permission notice appear in all copies.
  10.  
  11. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  12. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  14. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
  18.  
  19.  
  20. __author__ = "Stuart Cassoff"
  21. __license__ = __cow__
  22. __copyright__ = __cow__
  23. __version__ = "1.0"
  24. __when__ = "Winter/Spring 2024"
  25. __what__ = "pTycl"
  26.  
  27.  
  28. #
  29. # pTycl
  30. #
  31.  
  32.  
  33. import re
  34. import sys
  35. import evallers
  36.  
  37. def ptycl (blob, interp=None) {
  38. out = []
  39. level = 0
  40. indent = " "
  41. caret = True
  42. pblob = preprocess(blob)
  43. scanned = scan(pblob)
  44. for n,line in enumerate(pblob.splitlines()) {
  45. if n in scanned {
  46. o,p1,p2 = scanned[n]
  47. if o == -2 {
  48. continue
  49. }
  50. if o == -3 {
  51. out.append("#" + p1)
  52. continue
  53. }
  54. if o == 1 {
  55. line = p1 + " " + p2 + ":"
  56. out.append(indent * level + do_things_to(line, caret))
  57. } elif o == 0 {
  58. line = p1 + ("" if p2 == "" else " " + p2) + ":"
  59. out.append(indent * (level-1) + do_things_to(line, caret))
  60. }
  61. level += o
  62. continue
  63. }
  64. out.append(indent * level + do_things_to(line, caret).strip())
  65. }
  66. return "\n".join(out)
  67. }
  68.  
  69.  
  70. def scan (blob) {
  71. <?pragma lambdas off?>
  72. rup = re.compile(r"^\s*(async\s+def|async\s+for|async\s+with|case|class|def|for|if|match|try|with|while|mef|maf)\s+(.*?)\s*\{\s*$")
  73. rz1 = re.compile(r"^\s*\}\s*(elif|except)\s*(.+?)\s*\{.*$")
  74. rz2 = re.compile(r"^\s*\}\s*(else|finally)\s*\{.*$")
  75. rdn = re.compile(r"^\s*\}\s*(?:#\s*.*?)?\s*$")
  76. rpr = re.compile(r"\<\?" + "pragma" + r"\s*(.*?)\?\>", re.S)
  77. <?pragma lambdas on?>
  78. nobraces = False
  79. elide = False
  80. comment = False
  81. scanned = {}
  82. for n,line in enumerate(blob.splitlines()) {
  83. if (m := rpr.search(line)) {
  84. g = m.group(1).split(" ")
  85. if len(g) == 2 {
  86. if g[0] == "braces" {
  87. if g[1] == "on" {
  88. nobraces = False
  89. scanned[n] = (-2,None,None)
  90. } elif g[1] == "off" {
  91. nobraces = True
  92. scanned[n] = (-2,None,None)
  93. }
  94. } elif g[0] == "elide" {
  95. if g[1] == "on" {
  96. elide = True
  97. scanned[n] = (-2,None,None)
  98. continue
  99. } elif g[1] == "off" {
  100. elide = False
  101. scanned[n] = (-2,None,None)
  102. continue
  103. }
  104. } elif g[0] == "comment" {
  105. if g[1] == "on" {
  106. comment = True
  107. scanned[n] = (-2,None,None)
  108. continue
  109. } elif g[1] == "off" {
  110. comment = False
  111. scanned[n] = (-2,None,None)
  112. continue
  113. }
  114. }
  115. }
  116. }
  117. if nobraces : continue
  118. if elide {
  119. scanned[n] = (-2,None,None)
  120. continue
  121. }
  122. if comment {
  123. scanned[n] = (-3,line,None)
  124. continue
  125. }
  126. if (m := rup.match(line)) {
  127. scanned[n] = (1,m.group(1),m.group(2))
  128. } elif (m := rz1.match(line)) {
  129. scanned[n] = (0,m.group(1),m.group(2))
  130. } elif (m := rz2.match(line)) {
  131. scanned[n] = (0,m.group(1),"")
  132. } elif (m := rdn.match(line)) {
  133. scanned[n] = (-1,"","")
  134. }
  135. }
  136. return scanned
  137. }
  138.  
  139.  
  140. def preprocess (blob) {
  141. blob = preprocess_includes (blob)
  142. blob = preprocess_comments (blob)
  143. blob = preprocess_shells (blob)
  144. return blob
  145. }
  146.  
  147. def preprocess_includes (blob) {
  148. while (blob := do_preprocess_includes(nblob:=blob)) != nblob : pass
  149. return blob
  150. }
  151.  
  152. def do_preprocess_includes (blob) {
  153. reggy = re.compile(r"\<\?" + "include" + r"\s*(.*?)\?\>", re.S)
  154. if not (m := reggy.search(blob)): return blob
  155. for group in m.groups() {
  156. if (res := file_load(group, nonewline=True))[-1] {
  157. blob = blob[:m.span(0)[0]] + res[0] + blob[m.span(0)[1]:]
  158. }
  159. }
  160. return blob
  161. }
  162.  
  163. def preprocess_comments (blob, comment="//") {
  164. return "\n".join((line for line in blob.splitlines() if not line.strip().startswith(comment))) if comment in blob else blob
  165. }
  166.  
  167. def preprocess_shells (blob) {
  168. interp = None
  169. shells = {shell:re.compile(r"\<\?" + shell + r"\s*(.*?)\?\>", re.S) for shell in ('tcl','py','sh')}
  170. for shell,reggy in shells.items() {
  171. if not (m := reggy.search(blob)): continue
  172. if shell == 'tcl' {
  173. if interp is None {
  174. import tkinter
  175. interp = tkinter.Tcl()
  176. }
  177. cargo = interp
  178. } elif shell == 'py' {
  179. cargo = {}
  180. } else {
  181. cargo = None
  182. }
  183. for group in m.groups() {
  184. res,ok = evallers.__dict__['eval_' + shell](group, cargo)
  185. if shell == 'sh' {
  186. res = res.removesuffix("\n")
  187. }
  188. blob = blob[:m.span(0)[0]] + res + blob[m.span(0)[1]:]
  189. }
  190. }
  191. return blob
  192. }
  193.  
  194.  
  195. def do_things_to (line, caret=False) {
  196. line = mefalize (line)
  197. line = mafalize (line)
  198. line = lambdada (line)
  199. line = selfetate (line)
  200. if caret: line = djam_karet (line)
  201. return line
  202. }
  203.  
  204. <?pragma m_fs off?>
  205. def mefalize (line) : return re.sub(r"^mef(\s+.+?\()", r"def\1self, " , line).replace("(self, )", "(self)")
  206. def mafalize (line) : return re.sub(r"^maf(\s+.+?\()", r"async def\1self, " , line).replace("(self, )", "(self)")
  207. <?pragma m_fs on?>
  208.  
  209. def selfetate (line, fles=None) {
  210. if line == "" or "$" not in line {
  211. return line
  212. }
  213. if fles is None : fles = "self"
  214. if line[0] == "$" {
  215. line = fles + "." + line[1:]
  216. }
  217. for s,r in zip(*[iter((
  218. <?pragma self off?>
  219. " $" , " " + fles + "."
  220. , "\t$" , "\t" + fles + "."
  221. , "($)" , "(" + fles + ")"
  222. , "($," , "(" + fles + ","
  223. , "($" , "(" + fles + "."
  224. , "[$" , "[" + fles + "."
  225. , "{$" , "{" + fles + "."
  226. , ":$" , ":" + fles + "."
  227. , ",$" , "," + fles + "."
  228. <?pragma self on?>
  229. ))]*2): line = line.replace(s, r)
  230. return line
  231. }
  232.  
  233. def djam_karet (line) {
  234. if line == "" or "^" not in line {
  235. return line
  236. }
  237. if line[0] == "^" {
  238. line = "return" + line[1:]
  239. }
  240. for s,r in zip(*[iter((
  241. <?pragma carets off?>
  242. " ^ " , " return "
  243. , "\t^\t" , "\treturn\t"
  244. , " ^\t" , " return\t"
  245. , "\t^ " , "\treturn "
  246. , "#^ " , "#return "
  247. , "#^\t" , "#return\t"
  248. <?pragma carets on?>
  249. ))]*2): line = line.replace(s, r)
  250. return line
  251. }
  252.  
  253. def lambdada (line) {
  254. <?pragma lambdas off?>
  255. reggy = re.compile(r"\|\s*(.*?)\s*\|\s*(.+?)\s*\|")
  256. <?pragma lambdas on?>
  257. if not (m := reggy.search(line)): return line
  258. line = line[:m.span(0)[0]] + "lambda " + ",".join(m.group(1).split()) + ":" + m.group(2) + line[m.span(0)[1]:]
  259. return line
  260. }
  261.  
  262.  
  263. def main () {
  264. print(ptycl(sys.stdin.read()))
  265. }
  266.  
  267.  
  268. if __name__=="__main__":main()
  269.  
  270.  
  271. # EOF