config.bmx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. ' Copyright (c) 2013-2020 Bruce A Henderson
  2. '
  3. ' Based on the public domain Monkey "trans" by Mark Sibly
  4. '
  5. ' This software is provided 'as-is', without any express or implied
  6. ' warranty. In no event will the authors be held liable for any damages
  7. ' arising from the use of this software.
  8. '
  9. ' Permission is granted to anyone to use this software for any purpose,
  10. ' including commercial applications, and to alter it and redistribute it
  11. ' freely, subject to the following restrictions:
  12. '
  13. ' 1. The origin of this software must not be misrepresented; you must not
  14. ' claim that you wrote the original software. If you use this software
  15. ' in a product, an acknowledgment in the product documentation would be
  16. ' appreciated but is not required.
  17. '
  18. ' 2. Altered source versions must be plainly marked as such, and must not be
  19. ' misrepresented as being the original software.
  20. '
  21. ' 3. This notice may not be removed or altered from any source
  22. ' distribution.
  23. '
  24. SuperStrict
  25. Import BRL.LinkedList
  26. Import BRL.Map
  27. Import BRL.FileSystem
  28. Import Pub.zlib
  29. Import BRL.Math
  30. Import "options.bmx"
  31. Import "base.stringhelper.bmx"
  32. Import "base64.bmx"
  33. Import "enums.c"
  34. Import "hash.c"
  35. ' debugging help
  36. Const DEBUG:Int = False
  37. Const ABORT_ON_NULL:Int = True
  38. Const PROFILER:Int = False
  39. Const DEBUGSTOP_ON_ERROR:Int = False
  40. Const SHOW_INTERNALERR_LOCATION:Int = True
  41. Global ENV_LANG$
  42. Global _errInfo$
  43. Global _errStack:TList = New TList
  44. ' bytes offset to the first field
  45. Global OBJECT_BASE_OFFSET:Int = 8
  46. ' 4 bytes on 32-bit, 8 bytes on 64-bit
  47. Global POINTER_SIZE:Int = 4
  48. Global _symbols$[]=[ "..","[]",":*",":/",":+",":-",":|",":&",":~~",":shr",":shl",":sar",":mod"]
  49. Global _symbols_map$[]=[ "..","[]","*=","/=","+=","-=","|=","&=","^=",">>=", "<<=",">>=","%=" ]
  50. Global _fileHasher:TFileHash
  51. Function PushErr( errInfo$ )
  52. _errStack.AddLast _errInfo
  53. _errInfo=errInfo
  54. End Function
  55. Function PopErr()
  56. _errInfo=String(_errStack.RemoveLast())
  57. End Function
  58. Function Err( err$ )
  59. If DEBUGSTOP_ON_ERROR Then
  60. DebugStop ' useful for debugging!
  61. End If
  62. Throw "Compile Error: "+err + "~n" + _errInfo + "~n"
  63. End Function
  64. Function Warn( err$ )
  65. 'If DEBUGSTOP_ON_ERROR Then
  66. ' DebugStop ' useful for debugging!
  67. 'End If
  68. Print "Compile Warning: "+err + "~n" + _errInfo + "~n"
  69. End Function
  70. Function FormatError:String(path:String, line:Int, char:Int)
  71. Return "[" + path + ";" + line + ";" + char + "]"
  72. End Function
  73. Function InternalErr(errorLocation:String)
  74. If DEBUGSTOP_ON_ERROR Then
  75. DebugStop ' useful for debugging!
  76. End If
  77. Local locationMsg:String
  78. If SHOW_INTERNALERR_LOCATION And errorLocation Then locationMsg = " in " + errorLocation
  79. Throw "Compile Error: Internal Error" + locationMsg + ".~nPlease report the issue, with an example if possible, to https://github.com/bmx-ng/bcc/issues/new~n" + _errInfo + "~n"
  80. End Function
  81. Function IsSpace:Int( ch:Int )
  82. Return ch<=Asc(" ") Or ch=$A0 ' NO-BREAK SPACE (U+00A0)
  83. End Function
  84. Function IsDigit:Int( ch:Int )
  85. Return ch>=Asc("0") And ch<=Asc("9")
  86. End Function
  87. Function IsAlpha:Int( ch:Int )
  88. Return (ch>=Asc("A") And ch<=Asc("Z")) Or (ch>=Asc("a") And ch<=Asc("z"))
  89. End Function
  90. Function IsBinDigit:Int( ch:Int )
  91. Return ch=Asc("0") Or ch=Asc("1")
  92. End Function
  93. Function IsHexDigit:Int( ch:Int )
  94. Return IsDigit(ch) Or (ch>=Asc("A") And ch<=Asc("F")) Or (ch>=Asc("a") And ch<=Asc("f"))
  95. End Function
  96. Function Todo()
  97. Err "TODO!"
  98. End Function
  99. Function StringToLong:Long(value:String)
  100. Local Sign:Int = 1
  101. Local i:Int
  102. While i < value.length And (value[i] = Asc("+") Or value[i] = Asc("-"))
  103. If value[i] = Asc("-") Then
  104. Sign = -1
  105. End If
  106. i :+ 1
  107. Wend
  108. Local n:Long = 0
  109. While i < value.length
  110. Local c:Int = value[i]
  111. If Not IsDigit(c) Exit
  112. n = n * 10 + (c-Asc("0"))
  113. i :+ 1
  114. Wend
  115. Return n
  116. End Function
  117. Function IsStandardFunc:Int(func:String)
  118. func = func.ToLower()
  119. Global funcs:String = ";isalnum;isalpha;isascii;isblank;iscntrl;isdigit;isgraph;islower;isprint;ispunct;isspace;isupper;isxdigit;" + ..
  120. "strlen;_wgetenv;_wputenv;"
  121. Return funcs.Find(func) > 0
  122. End Function
  123. Function mapSymbol:String(sym:String)
  124. For Local i:Int = 0 Until _symbols.length
  125. If sym = _symbols[i] Then
  126. Return _symbols_map[i]
  127. End If
  128. Next
  129. Return sym
  130. End Function
  131. 'enquote depending on ENV_LANG
  132. '
  133. Function LangEnquote$( str$ )
  134. str=EscapeString(str)
  135. ' str=str.Replace( "~0","\0" ) 'Fix me?
  136. For Local i:Int=0 Until str.Length
  137. If str[i]>=32 And str[i]<128 Continue
  138. Local t$,n:Int=str[i]
  139. While n
  140. Local c:Int=(n&15)+48
  141. If c>=58 c:+97-58
  142. t=Chr( c )+t
  143. n=(n Shr 4) & $0fffffff
  144. Wend
  145. If Not t t="0"
  146. If ENV_LANG = "cpp" Then
  147. 'Case "cpp"
  148. t="~q~q\x"+t+"~q~q"
  149. Else
  150. t="\u"+("0000"+t)[-4..]
  151. End If
  152. str=str[..i]+t+str[i+1..]
  153. i:+t.Length-1
  154. Next
  155. str="~q"+str+"~q"
  156. If ENV_LANG="cpp" str="L"+str
  157. Return str
  158. End Function
  159. Function EscapeString$(str$)
  160. str=str.Replace( "\","\\" )
  161. str=str.Replace( "~q","\~q" )
  162. str=str.Replace( "~n","\n" )
  163. str=str.Replace( "~r","\r" )
  164. str=str.Replace( "~t","\t" )
  165. Return str
  166. End Function
  167. Function EscapeLines:String(str:String)
  168. str=str.Replace("~n", "Newline")
  169. Return str
  170. End Function
  171. Function BmxEnquote$( str$ )
  172. str=str.Replace( "~~","~~~~" )
  173. str=str.Replace( "~q","~~q" )
  174. str=str.Replace( "~n","~~n" )
  175. str=str.Replace( "~r","~~r" )
  176. str=str.Replace( "~t","~~t" )
  177. str=str.Replace( "~0","~~0" )
  178. str="~q"+str+"~q"
  179. Return str
  180. End Function
  181. Function BmxUnquote$( str$, unquoted:Int = False )
  182. Local length:Int
  183. Local i:Int
  184. If Not unquoted Then
  185. If str.length < 2 Or str[str.length - 1] <> Asc("~q") Then
  186. Err "Expecting expression but encountered malformed string literal"
  187. End If
  188. length = str.length - 1
  189. i = 1
  190. Else
  191. length = str.length
  192. End If
  193. Local sb:TStringBuffer = New TStringBuffer
  194. While i < length
  195. Local c:Int = str[i]
  196. i :+ 1
  197. If c <> Asc("~~") Then
  198. sb.AppendChar(c)
  199. Continue
  200. End If
  201. If i = length Err "Bad escape sequence in string"
  202. c = str[i]
  203. i :+ 1
  204. Select c
  205. Case Asc("~~")
  206. sb.AppendChar(c)
  207. Case Asc("0")
  208. sb.AppendChar(0)
  209. Case Asc("t")
  210. sb.AppendChar(Asc("~t"))
  211. Case Asc("r")
  212. sb.AppendChar(Asc("~r"))
  213. Case Asc("n")
  214. sb.AppendChar(Asc("~n"))
  215. Case Asc("q")
  216. sb.AppendChar(Asc("~q"))
  217. Case Asc("$") ' hex
  218. c = str[i]
  219. i :+ 1
  220. Local n:Int
  221. While True
  222. Local v:Int
  223. If c >= Asc("0") And c <= Asc("9") Then
  224. v = c-Asc("0")
  225. Else If c >= Asc("a") And c <= Asc("f") Then
  226. v = c-Asc("a")+10
  227. Else If c >= Asc("A") And c <= Asc("F") Then
  228. v = c-Asc("A")+10
  229. Else If c <> Asc("~~")
  230. Err "Bad escape sequence in string"
  231. Else
  232. Exit
  233. End If
  234. n = (n Shl 4) | (v & $f)
  235. If i = length Err "Bad escape sequence in string"
  236. c = str[i]
  237. i :+ 1
  238. Wend
  239. If c <> Asc("~~") Err "Bad escape sequence in string"
  240. sb.AppendChar(n)
  241. Case Asc("%") ' bin
  242. c = str[i]
  243. i :+ 1
  244. Local n:Int
  245. While c = Asc("1") Or c = Asc("0")
  246. n :Shl 1
  247. If c = Asc("1") Then
  248. n :| 1
  249. End If
  250. If i = length Err "Bad escape sequence in string"
  251. c = str[i]
  252. i :+ 1
  253. Wend
  254. If c <> Asc("~~") Err "Bad escape sequence in string"
  255. sb.AppendChar(n)
  256. Default
  257. If c >= Asc("1") And c <= Asc("9") Then
  258. Local n:Int
  259. While c >= Asc("0") And c <= Asc("9")
  260. n = n * 10 + (c-Asc("0"))
  261. If i = length Err "Bad escape sequence in string"
  262. c = str[i]
  263. i :+ 1
  264. Wend
  265. If c <> Asc("~~") Err "Bad escape sequence in string"
  266. sb.AppendChar(n)
  267. Else
  268. Err "Bad escape sequence in string"
  269. End If
  270. End Select
  271. Wend
  272. Return sb.ToString()
  273. End Function
  274. Function BmxProcessMultiString:String( str:String )
  275. Local valid:Int
  276. If str.length < 7 Then
  277. Err "Expecting expression but encountered malformed multiline string literal"
  278. End If
  279. For Local i:Int = 0 Until 3
  280. If str[i] <> Asc("~q") Or str[str.length -1 -i] <> Asc("~q") Then
  281. Err "Expecting expression but encountered malformed multiline string literal"
  282. End If
  283. Next
  284. str = str[3..str.length - 3]
  285. ' normalise line endings
  286. str = str.Replace("~r~n", "~n").Replace("~r", "~n")
  287. If str[0] <> Asc("~n") Then
  288. Err "Expecting EOL but encountered malformed multiline string literal"
  289. End If
  290. str = str[1..]
  291. Local LINES:String[] = str.Split("~n")
  292. Local lineCount:Int = LINES.length - 1
  293. Local last:String = LINES[lineCount]
  294. Local i:Int = last.length - 1
  295. While i >= 0
  296. If last[i] <> Asc(" ") And last[i] <> Asc("~t") Then
  297. Err "Expecting trailing whitespace"
  298. End If
  299. i :- 1
  300. Wend
  301. Local trailingIndent:String = last
  302. ' strip indent
  303. If trailingIndent Then
  304. For i = 0 Until lineCount
  305. Local line:String = LINES[i]
  306. If line.StartsWith(trailingIndent) Then
  307. line = line[trailingIndent.length..]
  308. LINES[i] = line
  309. End If
  310. Next
  311. End If
  312. ' right trim
  313. For i = 0 Until lineCount
  314. Local line:String = LINES[i]
  315. Local index:Int = line.length
  316. While index
  317. index :- 1
  318. If line[index] <> Asc(" ") And line[index] <> Asc("~t") Then
  319. Exit
  320. End If
  321. Wend
  322. If index < line.length - 1 Then
  323. line = line[..index + 1]
  324. LINES[i] = line
  325. End If
  326. Next
  327. Local sb:TStringBuffer = New TStringBuffer
  328. For i = 0 Until lineCount
  329. Local line:String = LINES[i]
  330. Local length:Int = line.length
  331. Local softWrap:Int
  332. If line And line[line.length-1] = Asc("\") Then
  333. softWrap = True
  334. length :- 1
  335. End If
  336. If line Then
  337. sb.Append(line[..length])
  338. End If
  339. If Not softWrap And i < lineCount - 1 Then
  340. sb.Append("~n")
  341. End If
  342. Next
  343. Return BmxUnquote(sb.ToString(), True)
  344. End Function
  345. Type TStack Extends TList
  346. Method Push(obj:Object)
  347. AddFirst(obj)
  348. End Method
  349. Method Length:Int()
  350. Return count()
  351. End Method
  352. Method Get:Object(index:Int)
  353. Return ValueAtIndex(index)
  354. End Method
  355. Method Pop:Object()
  356. Return RemoveFirst()
  357. End Method
  358. End Type
  359. Type TStringList Extends TList
  360. Method Join:String(s:String)
  361. Local arr:String[] = New String[count()]
  362. Local index:Int
  363. For Local t:String = EachIn Self
  364. arr[index] = t
  365. index :+ 1
  366. Next
  367. Return s.Join(arr)
  368. End Method
  369. End Type
  370. Type TKeyValue
  371. Field key:Object
  372. Field value:Object
  373. Method Create:TKeyValue(key:Object,value:Object)
  374. Self.key = key
  375. Self.value = value
  376. Return Self
  377. End Method
  378. Method Compare:Int(other:Object)
  379. If Not TKeyValue(other) Return 0
  380. Return key.Compare(TKeyValue(other).key)
  381. End Method
  382. End Type
  383. Type TUnorderedMap
  384. Field list:TList = New TList
  385. Field map:TMap = New TMap
  386. Field valuesList:TList = New TList
  387. Method Insert( key:Object,value:Object )
  388. list.AddLAst(New TKeyValue.Create(key, value))
  389. valuesList.AddLast(value)
  390. map.Insert(key, value)
  391. End Method
  392. Method Keys:TList()
  393. Local klist:TList = New TList
  394. For Local kv:TKeyValue = EachIn list
  395. klist.AddLast(kv.key)
  396. Next
  397. Return klist
  398. End Method
  399. Method Values:TList()
  400. 'Local vlist:TList = New TList
  401. 'For Local kv:TKeyValue = EachIn list
  402. ' vlist.AddLast(kv.value)
  403. 'Next
  404. Return valuesList
  405. End Method
  406. Method Contains:Int( key:Object )
  407. Return map.Contains(key)
  408. End Method
  409. Method ValueForKey:Object( key:Object )
  410. Return map.ValueForKey(key)
  411. End Method
  412. End Type
  413. Function MakeKeywords:String()
  414. Local keywords:String
  415. keywords :+ "import brl.classes~n"
  416. keywords :+ "Asc%(v$)=~qbrl_blitz_keywords_asc~q~n"
  417. keywords :+ "Chr$(v%)=~qbrl_blitz_keywords_chr~q~n"
  418. keywords :+ "Len%(v:Object)=~qbrl_blitz_keywords_len~q~n"
  419. keywords :+ "IncbinPtr@*(v$)=~qbbIncbinPtr~q~n"
  420. keywords :+ "IncbinLen%(v$)=~qbbIncbinLen~q~n"
  421. Return keywords
  422. End Function
  423. Function FilePath:String(path:String)
  424. Local baseDir:String = ExtractDir(path)
  425. Local bmxDir:String = baseDir + "/.bmx"
  426. If FileType(bmxDir) <> FILETYPE_DIR Then
  427. Throw "Missing : " + bmxDir
  428. End If
  429. Return bmxDir
  430. End Function
  431. Function BuildHeaderName:String(path:String)
  432. If opt_buildtype = BUILDTYPE_MODULE Then
  433. path = opt_modulename + "_" + StripDir(path)
  434. Else
  435. Local dir:String = ExtractDir(path).ToLower().Replace("/.bmx","")
  436. dir = dir[dir.findLast("/") + 1..]
  437. If dir.EndsWith(".mod") Then
  438. dir = dir.Replace(".mod", "")
  439. End If
  440. Local file:String = StripDir(path).ToLower()
  441. path = dir + "_" + file
  442. End If
  443. Return TStringHelper.Sanitize(path, , True)
  444. End Function
  445. Rem
  446. bbdoc: Get the header file name from a given module ident, optionally with include path.
  447. End Rem
  448. Function ModuleHeaderFromIdent:String(ident:String, includePath:Int = False)
  449. Local ns:String = ident[..ident.find(".")]
  450. Local name:String = ident[ident.find(".") + 1..]
  451. Local file:String = name + ".bmx" + FileMung() + ".h"
  452. If includePath Then
  453. file = ns + ".mod/" + name + ".mod/.bmx/" + file
  454. End If
  455. Return file
  456. End Function
  457. Function HeaderFile:String(path:String, mung:String)
  458. Local fileDir:String = FilePath(path)
  459. Local file:String = StripDir(path)
  460. Return fileDir + "/" + file + mung + ".h"
  461. End Function
  462. Function OutputFilePath:String(path:String, mung:String, suffix:String, bmxDir:Int = False)
  463. Local fileDir:String = FilePath(path)
  464. If bmxDir Then
  465. fileDir :+ "/.bmx"
  466. End If
  467. Local file:String = StripDir(path)
  468. Return fileDir + "/" + file + mung + "." + suffix
  469. End Function
  470. Function FileMung:String(makeApp:Int = False)
  471. Local m:String = "."
  472. If makeApp Then
  473. Select opt_apptype
  474. Case APPTYPE_CONSOLE
  475. m :+ "console."
  476. Case APPTYPE_GUI
  477. m :+ "gui."
  478. End Select
  479. End If
  480. If opt_release Then
  481. m :+ "release"
  482. Else
  483. m :+ "debug"
  484. End If
  485. ' If opt_threaded Then
  486. ' m :+ ".mt"
  487. ' End If
  488. m :+ "." + opt_platform
  489. m :+ "." + opt_arch
  490. Return m
  491. End Function
  492. Function HeaderComment:String()
  493. ' TODO
  494. End Function
  495. Global fileRegister:TMap = New TMap
  496. Function GenHash:String(file:String)
  497. Local Hash:String = bmx_gen_hash(file)
  498. If Not fileRegister.Contains(Hash) Then
  499. fileRegister.Insert(Hash, file)
  500. End If
  501. Return Hash
  502. End Function
  503. Type TTemplateRecord
  504. Field start:Int
  505. Field file:String
  506. Field source:String
  507. Method Create:TTemplateRecord(start:Int, file:String, source:String)
  508. Self.start = start
  509. Self.file = file
  510. Self.source = source
  511. Return Self
  512. End Method
  513. Method ToString:String()
  514. Local s:Byte Ptr = source.ToUTF8String()
  515. ?Not bmxng
  516. Local slen:Int = strlen_(s)
  517. ?bmxng
  518. Local slen:UInt = strlen_(s)
  519. ?
  520. ?Not bmxng
  521. Local dlen:Int = slen + 12
  522. ?bmxng And (win32 Or ptr32)
  523. Local dlen:UInt = slen + 12
  524. ?bmxng And ptr64 And Not win32
  525. Local dlen:ULong = slen + 12
  526. ?
  527. Local data:Byte[dlen]
  528. compress2(data, dlen, s, slen, 9)
  529. MemFree(s)
  530. Local t:String = "{" + start +","+ slen +","+ LangEnquote(file) + ","
  531. t :+ LangEnquote(TBase64.Encode(data, Int(dlen), 0, TBase64.DONT_BREAK_LINES))
  532. Return t + "}"
  533. End Method
  534. Function Load:TTemplateRecord(start:Int, file:String, size:Int, source:String)
  535. ?Not bmxng
  536. Local dlen:Int = size + 1
  537. ?bmxng And (win32 Or ptr32)
  538. Local dlen:UInt = size + 1
  539. ?bmxng And ptr64 And Not win32
  540. Local dlen:ULong = size + 1
  541. ?
  542. Local data:Byte[dlen]
  543. Local s:Byte[] = TBase64.Decode(source)
  544. ?Not bmxng
  545. uncompress(data, dlen, s, s.length)
  546. ?bmxng
  547. uncompress(data, dlen, s, UInt(s.length))
  548. ?
  549. Return New TTemplateRecord.Create(start, file, String.FromUTF8String(data))
  550. End Function
  551. End Type
  552. Type TCallback
  553. Method Callback(obj:Object) Abstract
  554. End Type
  555. Type TFileHash
  556. Field statePtr:Byte Ptr
  557. Method Create:TFileHash()
  558. statePtr = bmx_hash_createState()
  559. Return Self
  560. End Method
  561. Method CalculateHash:String(stream:TStream)
  562. Const BUFFER_SIZE:Int = 8192
  563. bmx_hash_reset(statePtr)
  564. Local data:Byte[BUFFER_SIZE]
  565. While True
  566. Local read:Int = stream.Read(data, BUFFER_SIZE)
  567. bmx_hash_update(statePtr, data, read)
  568. If read < BUFFER_SIZE Then
  569. Exit
  570. End If
  571. Wend
  572. Return bmx_hash_digest(statePtr)
  573. End Method
  574. End Type
  575. Function CalculateFileHash:String(path:String)
  576. If Not _fileHasher Then
  577. _fileHasher = New TFileHash.Create()
  578. End If
  579. If FileType(path) = FILETYPE_FILE Then
  580. Local stream:TStream = ReadStream(path)
  581. Local fileHash:String = _fileHasher.CalculateHash(stream)
  582. stream.Close()
  583. Return fileHash
  584. End If
  585. Return Null
  586. End Function
  587. Extern
  588. Function strlen_:Int(s:Byte Ptr)="strlen"
  589. Function bmx_enum_next_power(char:Int, val:Long Var, ret:Long Var)
  590. Function bmx_gen_hash:String(txt:String)
  591. Function bmx_hash_createState:Byte Ptr()
  592. Function bmx_hash_reset(state:Byte Ptr)
  593. Function bmx_hash_update(state:Byte Ptr, data:Byte Ptr, length:Int)
  594. Function bmx_hash_digest:String(state:Byte Ptr)
  595. End Extern