/red-system/loader.r

http://github.com/dockimbel/Red · R · 262 lines · 239 code · 20 blank · 3 comment · 20 complexity · 1fa378d2b9f4ae8559e51bdfc519ee0a MD5 · raw file

  1. REBOL [
  2. Title: "Red/System source program loader"
  3. Author: "Nenad Rakocevic"
  4. File: %loader.r
  5. Rights: "Copyright (C) 2011 Nenad Rakocevic. All rights reserved."
  6. License: "BSD-3 - https://github.com/dockimbel/Red/blob/master/BSD-3-License.txt"
  7. ]
  8. loader: context [
  9. verbose: 0
  10. include-dirs: none
  11. include-list: make hash! 20
  12. defs: make block! 100
  13. hex-chars: charset "0123456789ABCDEF"
  14. ws-chars: charset " ^M^-"
  15. ws-all: union ws-chars charset "^/"
  16. hex-delim: charset "[]()/"
  17. non-cbracket: complement charset "}^/"
  18. current-script: none
  19. line: none
  20. throw-error: func [err [string! block!]][
  21. print [
  22. "*** Loading Error:"
  23. either word? err [
  24. join uppercase/part mold err 1 " error"
  25. ][reform err]
  26. "^/*** in file:" mold current-script
  27. "^/*** at line:" line
  28. ]
  29. compiler/quit-on-error
  30. ]
  31. init: does [
  32. include-dirs: reduce [runtime-path]
  33. clear include-list
  34. clear defs
  35. insert defs <no-match> ;-- required to avoid empty rule (causes infinite loop)
  36. ]
  37. included?: func [file [file!]][
  38. file: get-modes file 'full-path
  39. either find include-list file [true][
  40. append include-list file
  41. false
  42. ]
  43. ]
  44. find-path: func [file [file!]][
  45. either slash = first file [
  46. if exists? file [return file] ;-- absolute path check
  47. ][
  48. foreach dir include-dirs [ ;-- relative path check using known directories
  49. if exists? dir/:file [return dir/:file]
  50. ]
  51. ]
  52. throw-error ["include file access error:" mold file]
  53. ]
  54. check-marker: func [src [string!] /local pos][
  55. unless parse/all src [any ws-all "Red/System" any ws-all #"[" to end][
  56. throw-error "not a Red/System source program"
  57. ]
  58. ]
  59. check-condition: func [type [word!] payload [block!]][
  60. if any [
  61. not any [word? payload/1 lit-word? payload/1]
  62. not in job payload/1
  63. all [type <> 'switch not find [= <> < > <= >=] payload/2]
  64. ][
  65. throw-error rejoin ["invalid #" type " condition"]
  66. ]
  67. either type = 'switch [
  68. any [
  69. select payload/2 job/(payload/1)
  70. select payload/2 #default
  71. ]
  72. ][
  73. do bind copy/part payload 3 job
  74. ]
  75. ]
  76. expand-string: func [src [string! binary!] /local value s e c lf-count ws i prev ins?][
  77. if verbose > 0 [print "running string preprocessor..."]
  78. line: 1 ;-- lines counter
  79. lf-count: [lf s: (
  80. if prev <> i: index? s [ ;-- workaround to avoid writing more complex rules
  81. prev: i
  82. line: line + 1
  83. if ins? [s: insert s rejoin [" #L " line " "]]
  84. ]
  85. )]
  86. ws: [ws-chars | (ins?: yes) lf-count]
  87. parse/all/case src [ ;-- not-LOAD-able syntax support
  88. any [
  89. (c: 0)
  90. #";" to lf
  91. | {"} thru {"}
  92. | "{" any [(ins?: no) lf-count | non-cbracket] "}"
  93. | ws s: ">>>" e: ws (
  94. e: change/part s "-**" e ;-- convert >>> to -**
  95. ) :e
  96. | ws s: #"%" e: ws (
  97. e: change/part s "///" e ;-- convert % to ///
  98. ) :e
  99. | [hex-delim | ws]
  100. s: copy value some [hex-chars (c: c + 1)] #"h" ;-- literal hexadecimal support
  101. e: [hex-delim | ws-all | #";" to lf | end] (
  102. either find [2 4 8] c [
  103. e: change/part s to integer! to issue! value e
  104. ][
  105. throw-error ["invalid hex literal:" copy/part s 40]
  106. ]
  107. ) :e
  108. | (ins?: yes) lf-count
  109. | skip
  110. ]
  111. ]
  112. ]
  113. expand-block: func [
  114. src [block!]
  115. /local blk rule name value s e opr then-block else-block cases body
  116. saved stack header mark idx prev
  117. ][
  118. if verbose > 0 [print "running block preprocessor..."]
  119. stack: append/only clear [] make block! 100
  120. append stack/1 1 ;-- insert root header starting size
  121. line: 1
  122. store-line: [
  123. header: last stack
  124. idx: index? s
  125. mark: to pair! reduce [line idx]
  126. either all [
  127. prev: pick tail header -1
  128. pair? prev
  129. prev/2 = idx ;-- test if previous marker is at the same series position
  130. ][
  131. change back tail header mark ;-- replace last marker by a more accurate one
  132. ][
  133. append header mark ;-- append line marker to header
  134. ]
  135. ]
  136. parse/case src blk: [
  137. s: (do store-line)
  138. some [
  139. defs ;-- resolve definitions in a single pass
  140. | s: #define set name word! set value skip e: (
  141. if verbose > 0 [print [mold name #":" mold value]]
  142. if word? value [value: to lit-word! value]
  143. either block? value [
  144. saved: reduce [s e]
  145. parse/case value rule: [
  146. some [defs | into rule | skip] ;-- resolve macros recursively
  147. ]
  148. set [s e] saved
  149. rule: copy/deep [s: _ e: (e: change/part s copy/deep _ e) :s]
  150. rule/4/5: :value
  151. ][
  152. rule: copy/deep [s: _ e: (e: change/part s _ e) :s]
  153. rule/4/4: :value
  154. ]
  155. rule/2: to lit-word! name
  156. either tag? defs/1 [remove defs][append defs '|]
  157. append defs rule
  158. remove/part s e
  159. ) :s
  160. | s: #include set name file! e: (
  161. either included? name: find-path name [
  162. s: remove/part s e ;-- already included, drop it
  163. ][
  164. if verbose > 0 [print ["...including file:" mold name]]
  165. value: skip process/short name 2 ;-- skip Red/System header
  166. e: change/part s value e
  167. insert e reduce [ ;-- put back the parent origin
  168. #script current-script
  169. ]
  170. insert s reduce [ ;-- mark code origin
  171. #script name
  172. ]
  173. current-script: name
  174. ]
  175. ) :s
  176. | s: #if set name word! set opr skip set value any-type! set then-block block! e: (
  177. either check-condition 'if reduce [name opr get/any 'value][
  178. change/part s then-block e
  179. ][
  180. remove/part s e
  181. ]
  182. ) :s
  183. | s: #either set name word! set opr skip set value any-type! set then-block block! set else-block block! e: (
  184. either check-condition 'either reduce [name opr get/any 'value][
  185. change/part s then-block e
  186. ][
  187. change/part s else-block e
  188. ]
  189. ) :s
  190. | s: #switch set name word! set cases block! e: (
  191. if body: check-condition 'switch reduce [name cases][
  192. change/part s body e
  193. ]
  194. ) :s
  195. | s: #L set line integer! e: (
  196. s: remove/part s 2
  197. new-line s yes
  198. do store-line
  199. ) :s
  200. | path! | set-path! | any-string! ;-- avoid diving into these series
  201. | s: (if any [block? s/1 paren? s/1][append/only stack copy [1]])
  202. [into blk | block! | paren!] ;-- black magic...
  203. s: (
  204. if any [block? s/-1 paren? s/-1][
  205. header: last stack
  206. change header length? header ;-- update header size
  207. s/-1: insert s/-1 header ;-- insert hidden header
  208. remove back tail stack
  209. ]
  210. )
  211. | skip
  212. ]
  213. ]
  214. change stack/1 length? stack/1 ;-- update root header size
  215. insert src stack/1 ;-- return source with hidden root header
  216. ]
  217. process: func [input [file! string!] /short /local src err path][
  218. if verbose > 0 [print ["processing" mold either file? input [input]['in-memory]]]
  219. if file? input [
  220. if all [
  221. %./ <> path: first split-path input ;-- is there a path in the filename?
  222. not find include-dirs path
  223. ][
  224. insert include-dirs path ;-- register source's dir as include dir
  225. ]
  226. if error? set/any 'err try [src: as-string read/binary input][ ;-- read source file
  227. throw-error ["file access error:" mold disarm err]
  228. ]
  229. ]
  230. unless short [
  231. current-script: pick reduce [input 'in-memory] file? input
  232. ]
  233. src: any [src input] ;-- process string-level compiler directives
  234. if file? input [check-marker src] ;-- look for "Red/System" head marker
  235. expand-string src
  236. if error? set/any 'err try [src: load src][ ;-- convert source to blocks
  237. throw-error ["syntax error during LOAD phase:" mold disarm err]
  238. ]
  239. unless short [src: expand-block src] ;-- process block-level compiler directives
  240. src
  241. ]
  242. ]