qrsc_to_tw.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #!/usr/bin/env python3
  2. import io, os, re, datetime
  3. regex_parts = {}
  4. regex_parts['operator_assign'] = r'=|\+=|-='
  5. regex_parts['parameter'] = r"\$ARGS\[\d+\]"
  6. regex_parts['var'] = r"\$?[a-z]+\w*"
  7. regex_parts['functioncall'] = r"\w+\s*\(\s*\w+\s*(?:,\s*\w*\s*)*\)"
  8. regex_parts['integer'] = r"\d+"
  9. regex_parts['string'] = r"""'\w'|"""+ r'''"\w"'''
  10. regex_parts['value_literal'] = "|".join([regex_parts['integer'],regex_parts['string']])
  11. regex_parts['value'] = "|".join([regex_parts['parameter'],regex_parts['var'],regex_parts['functioncall'],regex_parts['value_literal'] ])
  12. regex_parts['statement'] = '?'
  13. regex_parts['assignment'] = f"\s*({regex_parts['var']})\s*({regex_parts['operator_assign']})\s*({regex_parts['value']})\s*"
  14. def convert_command(command_raw):
  15. command = command_raw.strip()
  16. split_by_and = command.split(' & ')
  17. if len(split_by_and) > 1:
  18. return ' '.join([convert_command(s) for s in split_by_and])
  19. assign_operators = ['=','-=','+=']
  20. for assign_operator in assign_operators:
  21. split_by_assign = command.split(assign_operator)
  22. if len(split_by_assign) > 1:
  23. return f'<<set {convert_literal(split_by_assign[0])} {assign_operator} {convert_literal(split_by_assign[1])}>>'
  24. if match := re.match(r"^(\$?\w+)\s*(\+=|-=|=)\s*([\$']?\w*'?|\w+\s*\(\s*\w+\s*(?:,\s*\w*\s*)*\))$",command):
  25. return f'<<set {convert_literal(match.group(1))} {match.group(2)} {convert_literal(match.group(3))}>>'
  26. if match := re.match(r"^(gt|gs)\s+(.+)$",command):
  27. arguments = " ".join([convert_literal(l) for l in match.group(2).split(',')])
  28. return f'<<{match.group(1)} {arguments}>>'
  29. return ''
  30. def convert_condition(condition_raw):
  31. condition = condition_raw.strip()
  32. split_by_or = condition.split(' or ')
  33. if len(split_by_or) > 1:
  34. return ' or '.join([convert_condition(s) for s in split_by_or])
  35. split_by_xor = condition.split(' xor ')
  36. if len(split_by_xor) > 1:
  37. return ' xor '.join([convert_condition(s) for s in split_by_xor])
  38. split_by_and = condition.split(' and ')
  39. if len(split_by_and) > 1:
  40. return ' and '.join([convert_condition(s) for s in split_by_and])
  41. #match = re.match(r"(\$ARGS\[\d+\]|\$?\w+)\s*([=><]+)\s*('?\w+'?)",condition)
  42. #match = re.match(r"(\S+)\s*([=><]+)\s*(\S+)",condition)
  43. if(len(condition) >= 2 and condition[0] == '('):
  44. condition = condition[1:-1]
  45. match = re.match(r"([^<^>^=^!]+)\s*([=><!]+)\s*([^<^>^=^!]+)",condition)
  46. if match:
  47. left = convert_literal(match.group(1))
  48. right = convert_literal(match.group(3))
  49. operator = match.group(2)
  50. if operator == '=':
  51. operator = '=='
  52. elif operator == '!':
  53. operator = '!='
  54. return ' '.join([left,operator,right])
  55. return f'ERROR: FAILED TO CONVERT CONDITION: {condition}'
  56. def convert_literal(literal_raw):
  57. literal = literal_raw.strip()
  58. if not literal:
  59. return ''
  60. if literal == "''":
  61. return literal
  62. if(literal.isnumeric()):
  63. return literal
  64. if(len(literal)>= 3 and literal[0] == '\'' and literal[-1] == '\''):
  65. literal = literal.replace('<<','')
  66. literal = literal.replace('>>','')
  67. literal = literal.replace("''","'")
  68. return literal
  69. if(match := re.match(r"^arrsize\(\s*'(\$?)([a-z]+\w*)'\s*\)$",literal)):
  70. return f"${match.group(2)}.length"
  71. if(match := re.match(r"^killvar\s+'(\$?)([a-z]+\w*)'$",literal)):
  72. return f"<<set ${match.group(2)} to null>>"
  73. if(match := re.match(r'^\$ARGS\[(\d+)\]$',literal,re.I)):
  74. #ARGS
  75. return f'$location_var[$here][{match.group(1)}]'
  76. if(match := re.match(r"^(\$?)[a-zA-z]+\w*(\[('\w*'|\d+)\])?$",literal)):
  77. if match.group(1):
  78. return literal
  79. return '$'+literal
  80. if(match := re.match(r"^(\$?)([a-zA-z]+\w*)\[(.+)\]$",literal)):
  81. if match.group(1):
  82. return f"{match.group(2)}[{convert_literal(match.group(3))}]"
  83. return f"${match.group(2)}[{convert_literal(match.group(3))}]"
  84. if(match := re.match(r'^(\w+)\s*\((\s*\w+\s*(?:,\s*\w*\s*)*)\)$',literal)):
  85. function_name = match.group(1)
  86. function_parameters = match.group(2)
  87. return f'{function_name}({convert_literal(function_parameters)})'
  88. #Arithmetic Operations
  89. arith_operations = ['*','/','-','+','%',',']
  90. for arith_operation in arith_operations:
  91. split_by_arith = literal.split(arith_operation)
  92. if len(split_by_arith) > 1:
  93. return f' {arith_operation} '.join([convert_literal(s) for s in split_by_arith])
  94. return f'ERROR: FAILED TO CONVERT LITERAL: "{literal}"'
  95. def convert_file(filename,skipIfNotExplicit=0,defaultsubfolder=False):
  96. skip = skipIfNotExplicit
  97. qsp_filename = filename+".qsrc"
  98. qsp_filepath = os.path.join(qsp_sources_path,qsp_filename)
  99. tw_filename = filename+".tw"
  100. tw_filepath = os.path.join(tw_sources_path,tw_filename)
  101. if defaultsubfolder:
  102. os.makedirs(os.path.join(tw_sources_path,defaultsubfolder),exist_ok =True)
  103. tw_filepath = os.path.join(tw_sources_path,defaultsubfolder,tw_filename)
  104. try:
  105. with open(qsp_filepath) as file:
  106. lines = [line.rstrip() for line in file]
  107. except:
  108. try:
  109. with open(qsp_filepath, encoding="utf-8") as file:
  110. lines = [line.rstrip() for line in file]
  111. except:
  112. return f"FAILED: {qsp_filename}"
  113. location_identifier = ''
  114. if match := re.match(r"^\s*!!\s*(FOLDER\s*:\s*\w+)?\s*(SKIP\s*:\s*-?\d)?$",lines[0],re.I):
  115. if match.group(1):
  116. parts = match.group(1).split(':')
  117. new_path = os.path.join(tw_sources_path,parts[1])
  118. os.makedirs(new_path,exist_ok =True)
  119. tw_filepath = os.path.join(new_path,tw_filename)
  120. if match.group(2):
  121. parts = match.group(2).split(':')
  122. arg = int(parts[1])
  123. if arg == 1:
  124. skip = 1
  125. elif arg == 0:
  126. skip = 0
  127. elif arg == -1:
  128. skip = -1
  129. if skip == 1:
  130. return
  131. if skip == -1 and os.path.exists(tw_filepath):
  132. modification_time_delta = os.path.getmtime(qsp_filepath) - os.path.getmtime(tw_filepath)
  133. if modification_time_delta <= 0:
  134. return
  135. identifier_line = 0
  136. for line_num in range(0,len(lines)-1):
  137. line_raw = lines[line_num]
  138. line = line_raw.strip()
  139. match = re.match(r"#\s*(\S+)", line)
  140. if match:
  141. location_identifier = match.group(1)
  142. identifier_line = line_num
  143. break
  144. with open(tw_filepath, 'w') as file:
  145. #file.write(f'<!-- GENERATED: {datetime.datetime.now()} -->\n')
  146. file.write(f'::{location_identifier}\n')
  147. file.write(f"<<set $here = '{location_identifier}'>>\n")
  148. nesting = []
  149. for line_num in range(identifier_line+1,len(lines)-1):
  150. line_raw = lines[line_num]
  151. line = line_raw.strip()
  152. commentsplit = line.split("!!")
  153. if len(commentsplit) > 1:
  154. line = commentsplit[0]
  155. if not line:
  156. continue
  157. if line[0] == '!':
  158. continue
  159. #image-convert
  160. #line = re.sub(r'src=\"images\/([^\"]+)\"',r'''@src="$media_path+'\1'"''',line)
  161. line_output = ''
  162. whitespace_count = len(nesting)
  163. #file.write(whitespace_count * '\t')
  164. purges = [
  165. 'CLOSE ALL',
  166. 'close all',
  167. '*clr & cla',
  168. "gs 'stat'",
  169. 'killall',
  170. 'showstat 0','showstat 1',
  171. 'showobjs 0','showobjs 1',
  172. 'showinput 0','showinput 1'
  173. ]
  174. for purge in purges:
  175. line = line.replace(purge,'')
  176. if line.lower() == 'end':
  177. whitespace_count -= 1
  178. if len(nesting) > 0:
  179. end_command = nesting.pop()
  180. if end_command == 'if':
  181. #file.write('<</if>>\n')
  182. line_output = '<</if>>'
  183. elif end_command == 'while':
  184. #file.write('<</while>>\n')
  185. line_output = '<</while>>'
  186. elif end_command == 'act':
  187. #file.write('<</act>>\n')
  188. line_output = '<</act>>'
  189. else:
  190. print(f'ERROR: UNKNOWN NESTRING: {end_command}')
  191. else:
  192. line_output = '<</END>>'
  193. elif line.lower() == 'else':
  194. #file.write('<<else>>\n')
  195. line_output = '<<else>>'
  196. whitespace_count -= 1
  197. elif line.lower() == '*nl':
  198. #file.write('\n')
  199. line_output = ''
  200. elif line[0:3] == '---':
  201. line_output = ''
  202. elif match := re.match(r"""^'(?:<center>)?<img\s+(?:<<\$?\w+>>\s+)src="images\/([\w\/\.]+)(?:'\s*\+\s*rand\((\d+,\d+)\)\s*\+\s*')?([\w\/\.]+)"\s*>(?:<\/center>)?'""",line,re.I):
  203. #Images
  204. if match.group(2):
  205. line_output = f'''<<image "{match.group(1)}#{match.group(3)}" {match.group(2).replace(',',' ')}>>'''
  206. else:
  207. line_output = f'''<<image "{match.group(1)}{match.group(3)}">>'''
  208. elif match := re.match(r"""^'[\w<>\s=:'"\/\.\(\),\*]+'$""",line):
  209. #Plain HTML
  210. line_output = line[1:-1]
  211. if link_match := re.findall(r"""(<a href="exec:([^"]+)">([^<]+)<\/a>)""",line_output):
  212. #line_output = #line_output.replace(link_match[0],"moep")
  213. #print("moep")
  214. #print(link_match)
  215. for lmatch in link_match:
  216. line_output = line_output.replace(lmatch[0],f"""<<link "{lmatch[2]}">>{convert_command(lmatch[1])}<</link>>""")
  217. elif match := re.match(r"\s*(if|while)\s+([^:]+):(.*)",line,re.I):
  218. command = match.group(1).lower()
  219. line_w = f'<<{command} {convert_condition(match.group(2))}>>'
  220. if match.group(3):
  221. line_w += '\n' + (whitespace_count+1) * '\t'+convert_command(match.group(3))
  222. line_w += '\n' + whitespace_count * '\t'+f'<</{command}>>'
  223. else:
  224. nesting.append(command)
  225. #file.write(line_w)
  226. line_output = line_w
  227. elif match := re.match(r"\s*(act)\s+(.+):(.*)",line,re.I):
  228. # Act-Command
  229. command = match.group(1).lower()
  230. line_w = f'<<{command} {convert_literal(match.group(2))}>>'
  231. if match.group(3):
  232. line_w += '\n' + (whitespace_count+1) * '\t'+convert_command(match.group(3))
  233. line_w += '\n' + whitespace_count * '\t'+f'<</{command}>>'
  234. else:
  235. nesting.append(command)
  236. #file.write(line_w)
  237. line_output = line_w
  238. elif match := re.match(r"\s*(elseif)\s+([^:]+):(.*)",line,re.I):
  239. # ElseIf
  240. command = match.group(1).lower()
  241. line_w = f'<<{command} {convert_condition(match.group(2))}>>'
  242. if match.group(3):
  243. line_w += '\n' + whitespace_count * '\t'+f'<</if>>'
  244. #nesting.pop()
  245. #file.write(line_w)
  246. line_output = line_w
  247. elif match := convert_command(line):
  248. line_output = match
  249. else:
  250. line_output = convert_literal(line)
  251. if len(line_output) >= 2 and line_output[0] == '\'' and line_output[-1] == '\'':
  252. line_output = f'<p>{line_output[1:-1]}</p>'
  253. whitespace = whitespace_count * '\t'
  254. output = f'{whitespace}{line_output}\n'
  255. if output.strip():
  256. file.write(output)
  257. print(f"Completed {filename}")
  258. qsp_sources_path = "locations"
  259. tw_sources_path = os.path.join("sugarcube","src","autogenerated")
  260. os.makedirs(tw_sources_path, exist_ok=True)
  261. for file in os.listdir(qsp_sources_path):
  262. if file.endswith(".qsrc"):
  263. convert_file(os.path.splitext(file)[0],-1,'unsorted')