PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/red-system/compiler.r

http://github.com/dockimbel/Red
R | 2016 lines | 1833 code | 180 blank | 3 comment | 172 complexity | a2ec5dfbd4d730d87b0fd2e6b135bfd4 MD5 | raw file
  1. REBOL [
  2. Title: "Red/System compiler"
  3. Author: "Nenad Rakocevic"
  4. File: %compiler.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. do %utils/r2-forward.r
  9. do %utils/int-to-bin.r
  10. do %utils/virtual-struct.r
  11. do %utils/secure-clean-path.r
  12. do %linker.r
  13. do %emitter.r
  14. system-dialect: context [
  15. verbose: 0 ;-- logs verbosity level
  16. job: none ;-- reference the current job object
  17. runtime-path: %runtime/
  18. nl: newline
  19. loader: do bind load %loader.r 'self
  20. compiler: context [
  21. job: none ;-- shortcut for job object
  22. pc: none ;-- source code input cursor
  23. script: none ;-- source script file name
  24. none-type: [#[none]] ;-- marker for "no value returned"
  25. last-type: none-type ;-- type of last value from an expression
  26. locals: none ;-- currently compiled function specification block
  27. locals-init: [] ;-- currently compiler function locals variable init list
  28. func-name: none ;-- currently compiled function name
  29. block-level: 0 ;-- nesting level of input source block
  30. verbose: 0 ;-- logs verbosity level
  31. imports: make block! 10 ;-- list of imported functions
  32. natives: make hash! 40 ;-- list of functions to compile [name [specs] [body]...]
  33. globals: make hash! 40 ;-- list of globally defined symbols from scripts
  34. aliased-types: make hash! 10 ;-- list of aliased type definitions
  35. resolve-alias?: yes ;-- YES: instruct the type resolution function to reduce aliases
  36. debug-lines: reduce [ ;-- runtime source line/file information storage
  37. 'records make block! 1000 ;-- [address line file] records
  38. 'files make hash! 20 ;-- filenames table
  39. ]
  40. pos: none ;-- validation rules cursor for error reporting
  41. return-def: to-set-word 'return ;-- return: keyword
  42. fail: [end skip] ;-- fail rule
  43. rule: value: none ;-- global parsing rules helpers
  44. not-set!: [logic! integer!] ;-- reserved for internal use only
  45. number!: [byte! integer!] ;-- reserved for internal use only
  46. pointers!: [pointer! struct! c-string!] ;-- reserved for internal use only
  47. any-pointer!: union pointers! [function!] ;-- reserved for internal use only
  48. poly!: union number! pointers! ;-- reserved for internal use only
  49. any-type!: union poly! [logic!] ;-- reserved for internal use only
  50. type-sets: [not-set! number! poly! any-type! any-pointer!] ;-- reserved for internal use only
  51. comparison-op: [= <> < > <= >=]
  52. functions: to-hash [
  53. ;--Name--Arity--Type----Cc--Specs-- Cc = Calling convention
  54. + [2 op - [a [poly!] b [poly!] return: [poly!]]]
  55. - [2 op - [a [poly!] b [poly!] return: [poly!]]]
  56. * [2 op - [a [number!] b [number!] return: [number!]]]
  57. / [2 op - [a [number!] b [number!] return: [number!]]]
  58. and [2 op - [a [number!] b [number!] return: [number!]]]
  59. or [2 op - [a [number!] b [number!] return: [number!]]]
  60. xor [2 op - [a [number!] b [number!] return: [number!]]]
  61. // [2 op - [a [number!] b [number!] return: [number!]]] ;-- modulo
  62. /// [2 op - [a [number!] b [number!] return: [number!]]] ;-- remainder (real syntax: %)
  63. >> [2 op - [a [number!] b [number!] return: [number!]]] ;-- shift left signed
  64. << [2 op - [a [number!] b [number!] return: [number!]]] ;-- shift right signed
  65. -** [2 op - [a [number!] b [number!] return: [number!]]] ;-- shift right unsigned
  66. = [2 op - [a [any-type!] b [any-type!] return: [logic!]]]
  67. <> [2 op - [a [any-type!] b [any-type!] return: [logic!]]]
  68. > [2 op - [a [poly!] b [poly!] return: [logic!]]]
  69. < [2 op - [a [poly!] b [poly!] return: [logic!]]]
  70. >= [2 op - [a [poly!] b [poly!] return: [logic!]]]
  71. <= [2 op - [a [poly!] b [poly!] return: [logic!]]]
  72. not [1 inline - [a [not-set!] return: [logic!]]] ;@@ return should be not-set!
  73. push [1 inline - [a [any-type!]]]
  74. pop [0 inline - [ return: [integer!]]]
  75. ]
  76. user-functions: tail functions ;-- marker for user functions
  77. action-class: context [action: type: data: none]
  78. struct-syntax: [
  79. pos: opt [into ['align integer! opt ['big | 'little]]] ;-- struct's attributes
  80. pos: some [word! into type-spec] ;-- struct's members
  81. ]
  82. pointer-syntax: ['integer! | 'byte!]
  83. func-pointer: ['function! set value block! (check-specs '- value)]
  84. type-syntax: [
  85. 'logic! | 'int32! | 'integer! | 'uint8! | 'byte! | 'int16!
  86. | 'c-string!
  87. | 'pointer! into [pointer-syntax]
  88. | 'struct! into [struct-syntax]
  89. ]
  90. type-spec: [
  91. pos: some type-syntax | set value word! ( ;-- multiple types allowed for internal usage
  92. unless find aliased-types value [throw false] ;-- stop parsing if unresolved type
  93. )
  94. ]
  95. keywords: [
  96. ;& [throw-error "reserved for future use"]
  97. as [comp-as]
  98. assert [comp-assert]
  99. size? [comp-size?]
  100. if [comp-if]
  101. either [comp-either]
  102. case [comp-case]
  103. switch [comp-switch]
  104. until [comp-until]
  105. while [comp-while]
  106. any [comp-expression-list]
  107. all [comp-expression-list/_all]
  108. exit [comp-exit]
  109. return [comp-exit/value]
  110. declare [comp-declare]
  111. null [comp-null]
  112. true [also true pc: next pc] ;-- converts word! to logic!
  113. false [also false pc: next pc] ;-- converts word! to logic!
  114. func [raise-level-error "a function"] ;-- func declaration not allowed at this level
  115. function [raise-level-error "a function"] ;-- func declaration not allowed at this level
  116. alias [raise-level-error "an alias"] ;-- alias declaration not allowed at this level
  117. ]
  118. calc-line: has [idx head-end prev p header][
  119. header: head pc
  120. idx: (index? pc) - header/1 ;-- calculate real pc position (not counting hidden header)
  121. prev: 1
  122. parse header [ ;-- search for closest line marker
  123. skip ;-- skip over header length
  124. some [
  125. set p pair! (
  126. if p/2 = idx [return p/1] ;-- exact value position match
  127. if p/2 > idx [return prev] ;-- closest value position match
  128. prev: p/1
  129. )
  130. ]
  131. ]
  132. return p/1 ;-- return last marker
  133. ]
  134. store-dbg-lines: has [dbg pos][
  135. dbg: debug-lines
  136. unless pos: find dbg/files script [
  137. pos: tail dbg/files
  138. append dbg/files script
  139. ]
  140. repend dbg/records [
  141. emitter/tail-ptr calc-line index? pos
  142. ]
  143. ]
  144. quit-on-error: does [
  145. clean-up
  146. if system/options/args [quit/return 1]
  147. halt
  148. ]
  149. throw-error: func [err [word! string! block!]][
  150. print [
  151. "*** Compilation Error:"
  152. either word? err [
  153. join uppercase/part mold err 1 " error"
  154. ][reform err]
  155. "^/*** in file:" mold script
  156. either locals [join "^/*** in function: " func-name][""]
  157. ]
  158. if pc [
  159. print [
  160. "*** at line:" calc-line lf
  161. "*** near:" mold copy/part pc 8
  162. ]
  163. ]
  164. quit-on-error
  165. ]
  166. throw-warning: func [msg [string! block!] /near][
  167. print [
  168. "*** Warning:" reform msg
  169. "^/*** in:" mold script
  170. "^/*** at:" mold copy/part any [all [near back pc] pc] 8
  171. ]
  172. ]
  173. raise-level-error: func [kind [string!]][
  174. pc: back pc
  175. throw-error reform ["declaring" kind "at this level is not allowed"]
  176. ]
  177. raise-casting-error: does [
  178. backtrack 'as
  179. throw-error "multiple type casting not allowed"
  180. ]
  181. raise-paren-error: does [
  182. pc: back pc
  183. throw-error "parens are only allowed nested in an expression"
  184. ]
  185. raise-runtime-error: func [error [integer!]][
  186. emitter/target/emit-get-pc ;-- get current CPU program counter address
  187. last-type: [integer!] ;-- emit-get-pc returns an integer! (required for next line)
  188. compiler/comp-call '***-on-quit reduce [error <last>] ;-- raise a runtime error
  189. ]
  190. backtrack: func [value /local res][
  191. pc: any [res: find/only/reverse pc value pc]
  192. to logic! res
  193. ]
  194. blockify: func [value][either block? value [value][reduce [value]]]
  195. literal?: func [value][
  196. not any [word? value path? value block? value value = <last>]
  197. ]
  198. not-initialized?: func [name [word!] /local pos][
  199. all [
  200. locals
  201. pos: find locals /local
  202. pos: find next pos name
  203. not find locals-init name
  204. ]
  205. ]
  206. get-alias-id: func [pos [hash!]][
  207. 1000 + divide 1 + index? pos 2
  208. ]
  209. get-type-id: func [value /local type alias][
  210. with-alias-resolution off [type: resolve-expr-type value]
  211. either alias: find aliased-types type/1 [
  212. get-alias-id alias
  213. ][
  214. type: resolve-aliased type
  215. type: either type/1 = 'pointer! [
  216. pick [int-ptr! byte-ptr!] type/2/1 = 'integer!
  217. ][
  218. type/1
  219. ]
  220. select emitter/datatype-ID type
  221. ]
  222. ]
  223. system-reflexion?: func [path [path!] /local def][
  224. if path/1 = 'system [
  225. switch path/2 [
  226. alias [
  227. unless path/3 [
  228. backtrack path
  229. throw-error "invalid system/alias path access"
  230. ]
  231. unless def: find aliased-types path/3 [
  232. backtrack path
  233. throw-error ["undefined alias name:" path/3]
  234. ]
  235. last-type: [integer!]
  236. return get-alias-id def ;-- special encoding for aliases
  237. ]
  238. ; add new special reflective system path here
  239. ]
  240. ]
  241. none
  242. ]
  243. base-type?: func [value][
  244. if block? value [value: value/1]
  245. to logic! find/skip emitter/datatypes value 3
  246. ]
  247. unbox: func [value][
  248. either object? value [value/data][value]
  249. ]
  250. get-return-type: func [name [word!] /local type][
  251. type: select functions/:name/4 return-def
  252. unless type [
  253. backtrack name
  254. throw-error ["return type missing in function:" name]
  255. ]
  256. any [type none-type]
  257. ]
  258. set-last-type: func [spec [block!]][
  259. if spec: select spec return-def [last-type: spec]
  260. ]
  261. exists-variable?: func [name [word! set-word!]][
  262. name: to word! name
  263. to logic! any [
  264. all [locals find locals name]
  265. find globals name
  266. ]
  267. ]
  268. get-variable-spec: func [name [word!]][
  269. any [
  270. all [locals select locals name]
  271. select globals name
  272. ]
  273. ]
  274. get-arity: func [spec [block!] /local count][
  275. count: 0
  276. parse spec [opt block! any [word! block! (count: count + 1)]]
  277. count
  278. ]
  279. any-pointer?: func [type [block!]][
  280. type: first resolve-aliased type
  281. either find type-sets type [
  282. not empty? intersect get type any-pointer!
  283. ][
  284. to logic! find any-pointer! type
  285. ]
  286. ]
  287. equal-types?: func [type1 [word!] type2 [word!]][
  288. type1: either find type-sets type1 [get type1][reduce [type1]]
  289. type2: either find type-sets type2 [get type2][reduce [type2]]
  290. not empty? intersect type1 type2
  291. ]
  292. equal-types-list?: func [types [block!]][
  293. forall types [ ;-- check if all last expressions are of same type
  294. unless types/1/1 [return none-type] ;-- test if type is defined
  295. types/1: resolve-aliased types/1 ;-- reduce aliases and pseudo-types
  296. if all [
  297. not head? types
  298. not equal-types? types/-1/1 types/1/1
  299. ][
  300. return none-type
  301. ]
  302. ]
  303. first head types ;-- all types equal, return the first one
  304. ]
  305. with-alias-resolution: func [mode [logic!] body [block!] /local saved][
  306. saved: resolve-alias?
  307. resolve-alias?: mode
  308. do body
  309. resolve-alias?: saved
  310. ]
  311. resolve-aliased: func [type [block!] /local name][
  312. name: type/1
  313. all [
  314. not base-type? name
  315. not find type-sets name
  316. not type: select aliased-types name
  317. throw-error ["unknown type:" type]
  318. ]
  319. type
  320. ]
  321. resolve-type: func [name [word!] /with parent [block! none!] /local type][
  322. type: any [
  323. all [parent select parent name]
  324. get-variable-spec name
  325. ]
  326. if all [not type find functions name][
  327. return reduce ['function! functions/:name/4]
  328. ]
  329. unless any [not resolve-alias? base-type? type/1][
  330. type: select aliased-types type/1
  331. ]
  332. type
  333. ]
  334. resolve-struct-member-type: func [spec [block!] name [word!] /local type][
  335. unless type: select spec name [
  336. pc: skip pc -2
  337. throw-error [
  338. "invalid struct member" name "in:" mold to path! pc/1
  339. ]
  340. ]
  341. either resolve-alias? [resolve-aliased type][type]
  342. ]
  343. resolve-path-type: func [path [path! set-path!] /parent prev /local type path-error saved][
  344. path-error: [
  345. pc: skip pc -2
  346. throw-error "invalid path value"
  347. ]
  348. either word? path/1 [
  349. either parent [
  350. resolve-struct-member-type prev path/1 ;-- just check for correct member name
  351. with-alias-resolution on [
  352. type: resolve-type/with path/1 prev
  353. ]
  354. ][
  355. with-alias-resolution on [
  356. type: resolve-type path/1
  357. ]
  358. ]
  359. ][reduce [type?/word path/1]]
  360. unless type path-error
  361. either tail? skip path 2 [
  362. switch/default type/1 [
  363. c-string! [
  364. check-path-index path 'string
  365. [byte!]
  366. ]
  367. pointer! [
  368. check-path-index path 'pointer
  369. reduce [type/2/1] ;-- return pointed value type
  370. ]
  371. struct! [
  372. unless word? path/2 [
  373. backtrack path
  374. throw-error ["invalid struct member" path/2]
  375. ]
  376. resolve-struct-member-type type/2 path/2
  377. ]
  378. ] path-error
  379. ][
  380. resolve-path-type/parent next path second type
  381. ]
  382. ]
  383. get-type: func [value][
  384. switch/default type?/word value [
  385. none! [none-type] ;-- no type case (func with no return value)
  386. tag! [either value = <last> [last-type][ [logic!] ]]
  387. logic! [[logic!]]
  388. word! [resolve-type value]
  389. char! [[byte!]]
  390. integer! [[integer!]]
  391. string! [[c-string!]]
  392. path! [resolve-path-type value]
  393. object! [value/type]
  394. block! [
  395. either 'op = second select functions value/1 [
  396. either base-type? type: get-return-type value/1 [
  397. type ;-- unique returned type, stop here
  398. ][
  399. get-type value/2 ;-- recursively search for left operand base type
  400. ]
  401. ][
  402. get-return-type value/1
  403. ]
  404. ]
  405. paren! [
  406. reduce either all [value/1 = 'struct! word? value/2][
  407. [value/2]
  408. ][
  409. [value/1 value/2]
  410. ]
  411. ]
  412. get-word! [resolve-type to word! value]
  413. ][
  414. throw-error ["not accepted datatype:" type? value]
  415. ]
  416. ]
  417. resolve-expr-type: func [expr /quiet /local type func? spec][
  418. if block? expr [
  419. switch type?/word expr/1 [
  420. set-word! [expr: expr/2] ;-- resolve assigned value type
  421. set-path! [expr: to path! expr/1] ;-- resolve path type
  422. ]
  423. ]
  424. func?: all [
  425. block? expr word? expr/1
  426. not find comparison-op expr/1
  427. spec: select functions expr/1 ;-- works for unary & binary functions only!
  428. ]
  429. type: case [
  430. object? expr [
  431. expr/type ;-- type casting case
  432. ]
  433. all [func? find [op inline] spec/2][ ;-- works for unary & binary functions only!
  434. any [
  435. all [
  436. expr/1 <> 'not ;-- @@ issue with 'not return type
  437. spec: select spec/4 return-def
  438. base-type? spec/1 ;-- determined return type
  439. spec
  440. ]
  441. get-type expr/2 ;-- recursively search for return type
  442. ]
  443. ]
  444. all [func? quiet][
  445. any [
  446. select spec/4 return-def ;-- workaround error throwing in get-return-value
  447. none-type
  448. ]
  449. ]
  450. 'else [get-type expr]
  451. ]
  452. type
  453. ]
  454. cast: func [obj [object!] /local value ctype type][
  455. value: obj/data
  456. ctype: obj/type
  457. type: get-type value
  458. if type = ctype [
  459. throw-warning/near [
  460. "type casting from" type/1
  461. "to" ctype/1 "is not necessary"
  462. ] 'as
  463. ]
  464. if any [
  465. all [type/1 = 'function! ctype/1 <> 'integer!]
  466. all [ctype/1 = 'byte! find [c-string! pointer! struct!] type/1]
  467. all [
  468. find [c-string! pointer! struct!] ctype/1
  469. find [byte! logic!] type/1
  470. ]
  471. ][
  472. backtrack value
  473. throw-error [
  474. "type casting from" type/1
  475. "to" ctype/1 "is not allowed"
  476. ]
  477. ]
  478. unless literal? value [return value] ;-- shield the following literal conversions
  479. switch ctype/1 [
  480. byte! [
  481. switch type/1 [
  482. integer! [value: value and 255]
  483. logic! [value: pick [#"^(01)" #"^(00)"] value]
  484. ]
  485. ]
  486. integer! [
  487. if find [byte! logic!] type/1 [
  488. value: to integer! value
  489. ]
  490. ]
  491. logic! [
  492. switch type/1 [
  493. byte! [value: value <> null]
  494. integer! [value: value <> 0]
  495. ]
  496. ]
  497. ]
  498. value
  499. ]
  500. init-local: func [name [word!] expr casted [block! none!] /local pos type][
  501. append locals-init name ;-- mark as initialized
  502. pos: find locals name
  503. unless block? pos/2 [ ;-- if not typed, infer type
  504. insert/only at pos 2 type: any [
  505. casted
  506. resolve-expr-type expr
  507. ]
  508. if verbose > 2 [print ["inferred type" mold type "for variable:" pos/1]]
  509. ]
  510. ]
  511. add-symbol: func [name [word!] value type][
  512. unless type [type: get-type value]
  513. append globals reduce [name type: compose [(type)]]
  514. type
  515. ]
  516. add-function: func [type [word!] spec [block!] cc [word!]][
  517. repend functions [
  518. to word! spec/1 reduce [get-arity spec/3 type cc new-line/all spec/3 off]
  519. ]
  520. ]
  521. compare-func-specs: func [
  522. fun [word!] cb [get-word!] f-type [block!] c-type [block!] /local spec pos idx
  523. ][
  524. cb: to word! cb
  525. if functions/:cb/3 <> functions/:fun/3 [
  526. throw-error [
  527. "incompatible calling conventions between"
  528. fun "and" cb
  529. ]
  530. ]
  531. if pos: find f-type /local [f-type: head clear copy pos] ;-- remove locals
  532. if block? f-type/1 [f-type: next f-type] ;-- skip optional attributes block
  533. if block? c-type/1 [c-type: next c-type] ;-- skip optional attributes block
  534. idx: 2
  535. foreach [name type] f-type [
  536. if type <> c-type/:idx [return false]
  537. idx: idx + 2
  538. ]
  539. true
  540. ]
  541. check-keywords: func [name [word!]][
  542. if any [
  543. find keywords name
  544. name = 'comment
  545. ][
  546. throw-error ["attempt to redefined a protected keyword:" name]
  547. ]
  548. ]
  549. check-path-index: func [path [path! set-path!] type [word!] /local ending][
  550. ending: path/2
  551. case [
  552. all [type = 'pointer ending = 'value][] ;-- pass thru case
  553. word? ending [
  554. unless get-variable-spec ending [
  555. backtrack path
  556. throw-error ["undefined" type "index variable"]
  557. ]
  558. if 'integer! <> first resolve-type ending [
  559. backtrack path
  560. throw-error [
  561. "attempt to use" type
  562. "indexing with a non-integer! variable"
  563. ]
  564. ]
  565. ]
  566. not integer? ending [
  567. backtrack path
  568. throw-error [
  569. "attempt to use" type
  570. "indexing with a non-integer! value"
  571. ]
  572. ]
  573. ]
  574. ]
  575. check-func-name: func [name [word!] /only][
  576. if find functions name [
  577. pc: back pc
  578. throw-error ["attempt to redefine existing function name:" name]
  579. ]
  580. if all [not only find any [locals globals] name][
  581. pc: back pc
  582. throw-error ["a variable is already using the same name:" name]
  583. ]
  584. ]
  585. check-duplicates: func [
  586. name [word!] args [block! none!] locs [block! none!]
  587. /local dups
  588. ][
  589. if args [remove-each item args: copy args [not word? item]]
  590. if locs [remove-each item locs: copy locs [not word? item]]
  591. if any [
  592. all [args (length? unique args) <> length? args]
  593. all [locs (length? unique locs) <> length? locs]
  594. all [args locs not empty? dups: intersect args locs]
  595. ][
  596. throw-error [
  597. "duplicate variable definition in function" name
  598. either dups [reform ["for:" mold/only new-line/all dups no]][""]
  599. ]
  600. ]
  601. ]
  602. check-specs: func [
  603. name specs /extend
  604. /local type type-def spec-type attribs value args locs cconv
  605. ][
  606. unless block? specs [
  607. throw-error "function definition requires a specification block"
  608. ]
  609. cconv: ['cdecl | 'stdcall]
  610. attribs: [
  611. 'infix | 'variadic | 'typed | cconv
  612. | [cconv ['variadic | 'typed]]
  613. | [['variadic | 'typed] cconv]
  614. ]
  615. type-def: pick [[func-pointer | type-spec] [type-spec]] to logic! extend
  616. unless catch [
  617. parse specs [
  618. pos: opt [into attribs] ;-- functions attributes
  619. pos: copy args any [pos: word! into type-def] ;-- arguments definition
  620. pos: opt [ ;-- return type definition
  621. set value set-word! (
  622. rule: pick reduce [[into type-spec] fail] value = return-def
  623. ) rule
  624. ]
  625. pos: opt [/local copy locs some [pos: word! opt [into type-spec]]] ;-- local variables definition
  626. ]
  627. ][
  628. throw-error rejoin ["invalid definition for function " name ": " mold pos]
  629. ]
  630. check-duplicates name args locs
  631. ]
  632. check-conditional: func [name [word!] expr][
  633. if last-type/1 <> 'logic! [check-expected-type/key name expr [logic!]]
  634. ]
  635. check-expected-type: func [name [word!] expr expected [block!] /ret /key /local type alias][
  636. unless any [not none? expr key][return none] ;-- expr == none for special keywords
  637. if all [
  638. not all [object? expr expr/action = 'null] ;-- avoid null type resolution here
  639. not none? expr ;-- expr can be false, so explicit check for none is required
  640. first type: resolve-expr-type expr ;-- first => deep check that it's not [none]
  641. ][ ;-- check if a type is returned or none
  642. type: resolve-aliased type
  643. if alias: select aliased-types expected/1 [expected: alias]
  644. ]
  645. unless any [
  646. all [
  647. object? expr
  648. expr/action = 'null
  649. type: either expected/1 = 'any-type! [expr/type][expected] ;-- morph null type to expected
  650. any-pointer? expected
  651. ]
  652. all [
  653. type
  654. any [
  655. find type-sets expected/1
  656. find type-sets type/1
  657. ]
  658. equal-types? type/1 expected/1 ;-- internal polymorphic case
  659. ]
  660. all [
  661. type
  662. type/1 = 'function!
  663. expected/1 = 'function!
  664. compare-func-specs name expr type/2 expected/2 ;-- callback case
  665. ]
  666. expected = type ;-- normal single-type case
  667. ][
  668. if expected = type [type: 'null] ;-- make null error msg explicit
  669. any [
  670. backtrack any [all [block? expr expr/1] expr]
  671. backtrack name
  672. ]
  673. throw-error [
  674. reform case [
  675. ret [["wrong return type in function:" name]]
  676. key [[
  677. uppercase form name "requires a conditional expression"
  678. either find [while until] name ["as last expression"][""]
  679. ]]
  680. 'else [["argument type mismatch on calling:" name]]
  681. ]
  682. "^/*** expected:" join mold expected #","
  683. "found:" mold new-line/all any [type [none]] no
  684. ]
  685. ]
  686. type
  687. ]
  688. check-arguments-type: func [name args /local entry spec list][
  689. if find [set-word! set-path!] type?/word name [exit]
  690. entry: find functions name
  691. if all [
  692. not empty? spec: entry/2/4
  693. block? spec/1
  694. ][
  695. spec: next spec ;-- jump over attributes block
  696. ]
  697. list: []
  698. foreach arg args [
  699. append/only list check-expected-type name arg spec/2
  700. spec: skip spec 2
  701. ]
  702. if all [
  703. any [
  704. find emitter/target/comparison-op name
  705. find emitter/target/bitwise-op name
  706. ]
  707. not equal-types? list/1/1 list/2/1 ;-- allow implicit casting for math ops only
  708. ][
  709. backtrack name
  710. throw-error [
  711. "left and right argument must be of same type for:" name
  712. "^/*** left:" join list/1/1 #"," "right:" list/2/1
  713. ]
  714. ]
  715. if all [
  716. find emitter/target/math-op name
  717. any [
  718. all [list/1/1 = 'byte! any-pointer? list/2]
  719. all [list/2/1 = 'byte! any-pointer? list/1]
  720. ]
  721. ][
  722. backtrack name
  723. throw-error [
  724. "arguments must be of same size for:" name
  725. "^/*** left:" join list/1/1 #"," "right:" list/2/1
  726. ]
  727. ]
  728. clear list
  729. ]
  730. check-variable-arity?: func [spec [block!]][
  731. all [
  732. block? spec/1
  733. any [
  734. all [find spec/1 'variadic 'variadic]
  735. all [find spec/1 'typed 'typed]
  736. ]
  737. ]
  738. ]
  739. check-body: func [body][
  740. case/all [
  741. not block? :body [throw-error "expected a block of code"]
  742. empty? body [throw-error "expected a non-empty block of code"]
  743. ]
  744. ]
  745. fetch-into: func [code [block! paren!] body [block!] /local save-pc][ ;-- compile sub-block
  746. save-pc: pc
  747. pc: code
  748. do body
  749. next pc: save-pc
  750. ]
  751. fetch-func: func [name /local specs type cc][
  752. name: to word! name
  753. check-func-name name
  754. check-specs name specs: pc/2
  755. type: 'native
  756. cc: 'stdcall ;-- default calling convention
  757. if all [
  758. not empty? specs
  759. block? specs/1
  760. ][
  761. case [
  762. find specs/1 'infix [
  763. if 2 <> get-arity specs [
  764. throw-error [
  765. "infix function requires 2 arguments, found"
  766. get-arity specs "for" name
  767. ]
  768. ]
  769. type: 'infix
  770. ]
  771. find specs/1 'cdecl [cc: 'cdecl]
  772. find specs/1 'stdcall [cc: 'stdcall] ;-- get ready when fastcall will be the default cc
  773. ]
  774. ]
  775. add-function type reduce [name none specs] cc
  776. emitter/add-native name
  777. repend natives [name specs pc/3 script]
  778. pc: skip pc 3
  779. ]
  780. reduce-logic-tests: func [expr /local test value][
  781. test: [logic? expr/2 logic? expr/3]
  782. if all [
  783. block? expr
  784. find [= <>] expr/1
  785. any test
  786. ][
  787. expr: either all test [
  788. do expr ;-- let REBOL reduce the expression
  789. ][
  790. expr: copy expr
  791. if any [
  792. all [expr/1 = '= not all [expr/2 expr/3]]
  793. all [expr/1 = first [<>] any [expr/2 = true expr/3 = true]]
  794. ][
  795. insert expr 'not
  796. ]
  797. remove-each v expr [any [find [= <>] v logic? v]]
  798. if any [
  799. all [word? expr/1 get-variable-spec expr/1]
  800. paren? expr/1
  801. block? expr/1
  802. object? expr/1
  803. ][
  804. expr: expr/1 ;-- remove outer brackets if variable
  805. ]
  806. expr
  807. ]
  808. ]
  809. expr
  810. ]
  811. process-import: func [defs [block!] /local lib list cc name specs spec id reloc][
  812. unless block? defs [throw-error "#import expects a block! as argument"]
  813. unless parse defs [
  814. some [
  815. pos: set lib string! (
  816. unless list: select imports lib [
  817. repend imports [lib list: make block! 10]
  818. ]
  819. )
  820. pos: set cc ['cdecl | 'stdcall] ;-- calling convention
  821. pos: into [
  822. some [
  823. specs: ;-- new function mapping marker
  824. pos: set name set-word! (check-func-name name: to word! name)
  825. pos: set id string! (repend list [id reloc: make block! 1])
  826. pos: set spec block! (
  827. check-specs/extend name spec
  828. add-function 'import specs cc
  829. emitter/import-function name reloc
  830. )
  831. ]
  832. ]
  833. ]
  834. ][
  835. throw-error ["invalid import specification at:" pos]
  836. ]
  837. ]
  838. process-syscall: func [defs [block!] /local name id spec][
  839. unless block? defs [throw-error "#syscall expects a block! as argument"]
  840. unless parse defs [
  841. some [
  842. pos: set name set-word! (check-func-name name: to word! name)
  843. pos: set id integer!
  844. pos: set spec block! (
  845. check-specs/extend name spec
  846. add-function 'syscall reduce [name none spec] 'syscall
  847. append last functions id ;-- extend definition with syscode
  848. )
  849. ]
  850. ][
  851. throw-error ["invalid syscall specification at:" pos]
  852. ]
  853. ]
  854. comp-chunked: func [body [block!]][
  855. emitter/chunks/start
  856. do body
  857. emitter/chunks/stop
  858. ]
  859. comp-directive: has [body][
  860. switch/default pc/1 [
  861. #import [process-import pc/2 pc: skip pc 2]
  862. #syscall [process-syscall pc/2 pc: skip pc 2]
  863. #script [ ;-- internal compiler directive
  864. compiler/script: secure-clean-path pc/2 ;-- set the origin of following code
  865. pc: skip pc 2
  866. ]
  867. ][
  868. throw-error ["unknown directive" pc/1]
  869. ]
  870. ]
  871. comp-declare: has [rule value pos offset][
  872. unless find [set-word! set-path!] type?/word pc/-1 [
  873. throw-error "assignment expected before literal declaration"
  874. ]
  875. value: to paren! reduce either find [pointer! struct!] pc/2 [
  876. rule: get pick [struct-syntax pointer-syntax] pc/2 = 'struct!
  877. unless catch [parse pos: pc/3 rule][
  878. throw-error ["invalid literal syntax:" mold pos]
  879. ]
  880. offset: 3
  881. [pc/2 pc/3]
  882. ][
  883. unless all [word? pc/2 resolve-aliased reduce [pc/2]][
  884. throw-error [
  885. "declaring literal for type" pc/2 "not supported"
  886. ]
  887. ]
  888. offset: 2
  889. ['struct! pc/2]
  890. ]
  891. pc: skip pc offset
  892. value
  893. ]
  894. comp-null: does [
  895. pc: next pc
  896. make action-class [action: 'null type: [any-pointer!] data: 0]
  897. ]
  898. comp-as: has [ctype ptr? expr][
  899. ctype: pc/2
  900. if ptr?: find [pointer! struct!] ctype [ctype: reduce [pc/2 pc/3]]
  901. unless any [
  902. parse blockify ctype type-syntax
  903. find aliased-types ctype
  904. ][
  905. throw-error ["invalid target type casting:" ctype]
  906. ]
  907. pc: skip pc pick [3 2] to logic! ptr?
  908. expr: fetch-expression
  909. if all [object? expr expr/action = 'null][
  910. pc: back pc
  911. throw-error "type casting on null value is not allowed"
  912. ]
  913. make action-class [
  914. action: 'type-cast
  915. type: blockify ctype
  916. data: expr
  917. ]
  918. ]
  919. comp-assert: has [expr line][
  920. either job/debug? [
  921. line: calc-line
  922. pc: next pc
  923. expr: fetch-expression/final
  924. check-conditional 'assert expr ;-- verify conditional expression
  925. expr: process-logic-encoding expr yes
  926. insert/only pc next next compose [
  927. 2 (to pair! reduce [line 1]) ;-- hidden line offset header
  928. ***-on-quit 98 as integer! system/pc
  929. ]
  930. set [unused chunk] comp-block-chunked ;-- compile TRUE block
  931. emitter/set-signed-state expr ;-- properly set signed/unsigned state
  932. emitter/branch/over/on chunk reduce [expr/1] ;-- branch over if expr is true
  933. emitter/merge chunk
  934. last-type: none-type
  935. <last>
  936. ][
  937. pc: next pc
  938. fetch-expression ;-- consume next expression
  939. none
  940. ]
  941. ]
  942. comp-alias: has [name][
  943. unless set-word? pc/-1 [
  944. throw-error "assignment expected for ALIAS"
  945. ]
  946. unless pc/2 = 'struct! [
  947. throw-error "ALIAS only works on struct! type"
  948. ]
  949. if find aliased-types name: to word! pc/-1 [
  950. pc: back pc
  951. throw-error reform [
  952. "alias name already defined as:"
  953. mold aliased-types/:name
  954. ]
  955. ]
  956. if base-type? name [
  957. pc: back pc
  958. throw-error "a base type name cannot be defined as an alias name"
  959. ]
  960. repend aliased-types [name reduce [pc/2 pc/3]]
  961. unless catch [parse pos: pc/3 struct-syntax][
  962. throw-error ["invalid struct syntax:" mold pos]
  963. ]
  964. pc: skip pc 3
  965. none
  966. ]
  967. comp-size?: has [type expr][
  968. pc: next pc
  969. unless all [
  970. word? expr: pc/1
  971. type: any [
  972. all [base-type? expr expr]
  973. select aliased-types expr
  974. ]
  975. pc: next pc
  976. ][
  977. expr: fetch-expression/final
  978. type: resolve-expr-type expr
  979. ]
  980. emitter/get-size type expr
  981. ]
  982. comp-exit: func [/value /local expr type ret][
  983. unless locals [
  984. throw-error [pc/1 "is not allowed outside of a function"]
  985. ]
  986. pc: next pc
  987. ret: select locals return-def
  988. either value [
  989. unless ret [ ;-- check if return: declared
  990. throw-error [
  991. "RETURN keyword used without return: declaration in"
  992. func-name
  993. ]
  994. ]
  995. expr: fetch-expression/final/keep ;-- compile expression to return
  996. type: check-expected-type/ret func-name expr ret
  997. ret: either type [last-type: type <last>][none]
  998. ][
  999. if ret [
  1000. throw-error [
  1001. "EXIT keyword is not compatible with declaring a return value"
  1002. ]
  1003. ]
  1004. ]
  1005. emitter/target/emit-exit
  1006. ret
  1007. ]
  1008. comp-block-chunked: func [/only /test name [word!] /local expr][
  1009. emitter/chunks/start
  1010. expr: either only [
  1011. fetch-expression/final ;-- returns first expression
  1012. ][
  1013. comp-block/final ;-- returns last expression
  1014. ]
  1015. if test [
  1016. check-conditional name expr ;-- verify conditional expression
  1017. expr: process-logic-encoding expr no
  1018. ]
  1019. reduce [
  1020. expr
  1021. emitter/chunks/stop ;-- returns a chunk block!
  1022. ]
  1023. ]
  1024. process-logic-encoding: func [expr invert? [logic!]][ ;-- preprocess logic values
  1025. case [
  1026. logic? expr [ [#[true]] ]
  1027. find [word! path!] type?/word expr [
  1028. emitter/target/emit-operation '= [<last> 0]
  1029. reduce [not invert?]
  1030. ]
  1031. object? expr [
  1032. expr: cast expr
  1033. unless find [word! path!] type?/word any [
  1034. all [block? expr expr/1] expr
  1035. ][
  1036. emitter/target/emit-operation '= [<last> 0]
  1037. ]
  1038. process-logic-encoding expr invert?
  1039. ]
  1040. block? expr [
  1041. case [
  1042. find comparison-op expr/1 [expr]
  1043. 'else [process-logic-encoding expr/1 invert?]
  1044. ]
  1045. ]
  1046. tag? expr [
  1047. either last-type/1 = 'logic! [
  1048. emitter/target/emit-operation '= [<last> 0]
  1049. reduce [not invert?]
  1050. ][expr]
  1051. ]
  1052. 'else [expr]
  1053. ]
  1054. ]
  1055. comp-if: has [expr unused chunk][
  1056. pc: next pc
  1057. expr: fetch-expression/final ;-- compile expression
  1058. check-conditional 'if expr ;-- verify conditional expression
  1059. expr: process-logic-encoding expr no
  1060. check-body pc/1 ;-- check TRUE block
  1061. set [unused chunk] comp-block-chunked ;-- compile TRUE block
  1062. emitter/set-signed-state expr ;-- properly set signed/unsigned state
  1063. emitter/branch/over/on chunk expr/1 ;-- insert IF branching
  1064. emitter/merge chunk
  1065. last-type: none-type
  1066. <last>
  1067. ]
  1068. comp-either: has [expr e-true e-false c-true c-false offset t-true t-false][
  1069. pc: next pc
  1070. expr: fetch-expression/final ;-- compile expression
  1071. check-conditional 'either expr ;-- verify conditional expression
  1072. expr: process-logic-encoding expr no
  1073. check-body pc/1 ;-- check TRUE block
  1074. check-body pc/2 ;-- check FALSE block
  1075. set [e-true c-true] comp-block-chunked ;-- compile TRUE block
  1076. set [e-false c-false] comp-block-chunked ;-- compile FALSE block
  1077. offset: emitter/branch/over c-false
  1078. emitter/set-signed-state expr ;-- properly set signed/unsigned state
  1079. emitter/branch/over/adjust/on c-true negate offset expr/1 ;-- skip over JMP-exit
  1080. emitter/merge emitter/chunks/join c-true c-false
  1081. t-true: resolve-expr-type/quiet e-true
  1082. t-false: resolve-expr-type/quiet e-false
  1083. last-type: either all [
  1084. t-true/1 t-false/1
  1085. t-true: resolve-aliased t-true ;-- alias resolution is safe here
  1086. t-false: resolve-aliased t-false
  1087. equal-types? t-true/1 t-false/1
  1088. ][t-true][none-type] ;-- allow nesting if both blocks return same type
  1089. <last>
  1090. ]
  1091. comp-case: has [cases list test body op bodies offset types][
  1092. pc: next pc
  1093. check-body cases: pc/1
  1094. list: make block! 8
  1095. types: make block! 8
  1096. until [ ;-- collect and pre-compile all cases
  1097. fetch-into cases [ ;-- compile case test
  1098. append/only list comp-block-chunked/only/test 'case
  1099. cases: pc ;-- set cursor after the expression
  1100. ]
  1101. check-body cases/1
  1102. fetch-into cases [ ;-- compile case body
  1103. append/only list body: comp-block-chunked
  1104. append/only types resolve-expr-type/quiet body/1
  1105. ]
  1106. tail? cases: next cases
  1107. ]
  1108. bodies: comp-chunked [raise-runtime-error 100] ;-- raise a runtime error if unmatched value
  1109. list: tail list ;-- point to last case test
  1110. until [ ;-- left join all cases in reverse order
  1111. list: skip list -2
  1112. set [test body] list ;-- retrieve case-test and case-body chunks
  1113. emitter/set-signed-state test/1 ;-- properly set signed/unsigned state
  1114. offset: negate emitter/branch/over bodies ;-- insert case exit branching
  1115. emitter/branch/over/on/adjust body/2 test/1/1 offset ;-- insert case test branching
  1116. body: emitter/chunks/join test/2 body/2 ;-- join case test with case body
  1117. bodies: emitter/chunks/join body bodies ;-- left join case with other cases
  1118. head? list
  1119. ]
  1120. emitter/merge bodies ;-- commit all to main code buffer
  1121. pc: next pc
  1122. last-type: equal-types-list? types ;-- test if usage in expression allowed
  1123. <last>
  1124. ]
  1125. comp-switch: has [expr save-type spec value values body bodies list types default][
  1126. pc: next pc
  1127. expr: fetch-expression/final ;-- compile argument
  1128. if any [none? expr last-type = none-type][
  1129. throw-error "SWITCH argument has no return value"
  1130. ]
  1131. save-type: last-type
  1132. check-body spec: pc/1
  1133. foreach w [values list types][set w make block! 8]
  1134. ;-- check syntax and store parts in different lists
  1135. unless parse spec [
  1136. some [
  1137. pos: copy value some [integer! | char!]
  1138. (repend values [value none]) ;-- [value body-offset ...]
  1139. pos: block! (
  1140. fetch-into pos [ ;-- compile action body
  1141. body: comp-block-chunked
  1142. append/only list body/2
  1143. append/only types resolve-expr-type/quiet body/1
  1144. ]
  1145. )
  1146. ]
  1147. opt [
  1148. 'default pos: block! (
  1149. fetch-into pos [ ;-- compile default body
  1150. default: comp-block-chunked
  1151. append/only types resolve-expr-type/quiet default/1
  1152. ]
  1153. )
  1154. ]
  1155. ][
  1156. throw-error ["wrong syntax in SWITCH block at:" copy/part pos 4]
  1157. ]
  1158. ;-- assemble all actions together, with exit at end for each one
  1159. bodies: emitter/chunks/empty
  1160. list: tail list ;-- point to last action
  1161. until [ ;-- left join all actions in reverse order
  1162. body: first list: back list
  1163. unless empty? bodies/1 [
  1164. emitter/branch/over bodies ;-- insert case exit branching
  1165. ]
  1166. bodies: emitter/chunks/join body bodies ;-- left join action with other actions
  1167. change at values 2 * index? list length? bodies/1
  1168. head? list
  1169. ]
  1170. ;-- insert default clause or jump to runtime error
  1171. either default [
  1172. emitter/branch/over bodies ;-- insert default exit branching
  1173. bodies: emitter/chunks/join default/2 bodies ;-- insert default action
  1174. ][
  1175. body: comp-chunked [raise-runtime-error 101] ;-- raise a runtime error if unmatched value
  1176. bodies: emitter/chunks/join body bodies
  1177. ]
  1178. ;-- construct tests + branching and insert them at head
  1179. last-type: save-type
  1180. emitter/set-signed-state expr ;-- properly set signed/unsigned state
  1181. values: tail values
  1182. until [
  1183. values: skip values -2
  1184. foreach v values/1 [ ;-- process multiple values per action
  1185. body: comp-chunked [
  1186. emitter/target/emit-operation '= reduce [<last> v]
  1187. ]
  1188. emitter/branch/over/on/adjust bodies [=] values/2 ;-- insert action branching
  1189. bodies: emitter/chunks/join body bodies
  1190. ]
  1191. head? values
  1192. ]
  1193. emitter/merge bodies ;-- commit all to main code buffer
  1194. pc: next pc
  1195. last-type: equal-types-list? types ;-- test if usage in expression allowed
  1196. <last>
  1197. ]
  1198. comp-until: has [expr chunk][
  1199. pc: next pc
  1200. check-body pc/1
  1201. set [expr chunk] comp-block-chunked/test 'until
  1202. emitter/branch/back/on chunk expr/1
  1203. emitter/merge chunk
  1204. last-type: none-type
  1205. <last>
  1206. ]
  1207. comp-while: has [expr unused cond body offset bodies][
  1208. pc: next pc
  1209. check-body pc/1 ;-- check condition block
  1210. check-body pc/2 ;-- check body block
  1211. set [expr cond] comp-block-chunked/test 'while ;-- Condition block
  1212. set [unused body] comp-block-chunked ;-- Body block
  1213. if logic? expr/1 [expr: [<>]] ;-- re-encode test op
  1214. offset: emitter/branch/over body ;-- Jump to condition
  1215. bodies: emitter/chunks/join body cond
  1216. emitter/set-signed-state expr ;-- properly set signed/unsigned state
  1217. emitter/branch/back/on/adjust bodies reduce [expr/1] offset ;-- Test condition, exit if FALSE
  1218. emitter/merge bodies
  1219. last-type: none-type
  1220. <last>
  1221. ]
  1222. comp-expression-list: func [/_all /local list offset bodies op][
  1223. pc: next pc
  1224. check-body pc/1 ;-- check body block
  1225. list: make block! 8
  1226. pc: fetch-into pc/1 [
  1227. while [not tail? pc][ ;-- comp all expressions in chunks
  1228. append/only list comp-block-chunked/only/test pick [all any] to logic! _all
  1229. ]
  1230. ]
  1231. list: back tail list
  1232. set [offset bodies] emitter/chunks/make-boolean ;-- emit ending FALSE/TRUE block
  1233. if _all [emitter/branch/over/adjust bodies offset/1] ;-- conclude by a branch on TRUE
  1234. offset: pick offset not _all ;-- branch to TRUE or FALSE
  1235. until [ ;-- left join all expr in reverse order
  1236. op: either logic? list/1/1/1 [first [<>]][list/1/1/1]
  1237. unless _all [op: reduce [op]] ;-- do not invert the test if ANY
  1238. emitter/set-signed-state list/1/1 ;-- properly set signed/unsigned state
  1239. emitter/branch/over/on/adjust bodies op offset ;-- first emit branch
  1240. bodies: emitter/chunks/join list/1/2 bodies ;-- then left join expr
  1241. also head? list list: back list
  1242. ]
  1243. emitter/merge bodies
  1244. last-type: [logic!]
  1245. <last>
  1246. ]
  1247. comp-assignment: has [name value n][
  1248. name: pc/1
  1249. pc: next pc
  1250. if set-word? name [
  1251. check-keywords n: to word! name ;-- forbid keywords redefinition
  1252. if get-word? pc/1 [
  1253. throw-error "storing a function! requires a type casting"
  1254. ]
  1255. unless all [locals find locals n][
  1256. check-func-name/only n ;-- avoid clashing with an existing function name
  1257. ]
  1258. ]
  1259. either none? value: fetch-expression [ ;-- explicitly test for none!
  1260. none
  1261. ][
  1262. new-line/all reduce [name value] no
  1263. ]
  1264. ]
  1265. comp-path: has [path value][
  1266. path: pc/1
  1267. comp-word/path path/1 ;-- check if root word is defined
  1268. unless value: system-reflexion? path [
  1269. last-type: resolve-path-type path
  1270. ]
  1271. any [value path]
  1272. ]
  1273. comp-get-word: has [spec][
  1274. unless spec: select functions to word! pc/1 [
  1275. throw-error ["function" to word! pc/1 "not defined"]
  1276. ]
  1277. unless spec/2 = 'native [
  1278. throw-error "get-word syntax only reserved for native functions for now"
  1279. ]
  1280. unless spec/5 = 'callback [append spec 'callback]
  1281. also pc/1 pc: next pc
  1282. ]
  1283. comp-word: func [/path symbol [word!] /local entry args n name expr attribute fetch][
  1284. name: any [symbol pc/1]
  1285. case [
  1286. entry: select keywords name [do entry] ;-- it's a reserved word
  1287. any [
  1288. all [locals find locals name]
  1289. find globals name
  1290. ][ ;-- it's a variable
  1291. if not-initialized? name [
  1292. throw-error ["local variable" name "used before being initialized!"]
  1293. ]
  1294. last-type: resolve-type name
  1295. also name pc: next pc
  1296. ]
  1297. all [
  1298. not path
  1299. entry: find functions name
  1300. ][
  1301. pc: next pc ;-- it's a function
  1302. either attribute: check-variable-arity? entry/2/4 [
  1303. fetch: [
  1304. append/only args fetch-expression
  1305. if attribute = 'typed [
  1306. append args get-type-id last args
  1307. ]
  1308. ]
  1309. args: make block! 1
  1310. either block? pc/1 [
  1311. fetch-into pc/1 [until [do fetch tail? pc]]
  1312. pc: next pc ;-- jump over arguments block
  1313. ][
  1314. do fetch
  1315. ]
  1316. reduce [name to-issue attribute args]
  1317. ][ ;-- fixed arity case
  1318. args: make block! n: entry/2/1
  1319. loop n [append/only args fetch-expression] ;-- fetch n arguments
  1320. head insert args name
  1321. ]
  1322. ]
  1323. 'else [throw-error ["undefined symbol:" mold name]]
  1324. ]
  1325. ]
  1326. cast-null: func [variable [set-word! set-path!] /local casting][
  1327. unless all [
  1328. attempt [
  1329. casting: get-type any [
  1330. all [set-word? variable to word! variable]
  1331. to path! variable
  1332. ]
  1333. ]
  1334. any-pointer? casting
  1335. ][
  1336. backtrack variable
  1337. throw-error "Invalid null assignment"
  1338. ]
  1339. casting
  1340. ]
  1341. order-args: func [name [word!] args [block!]][
  1342. if any [
  1343. all [
  1344. find [import native infix] functions/:name/2
  1345. find [stdcall cdecl] functions/:name/3
  1346. ]
  1347. all [
  1348. functions/:name/2 = 'syscall
  1349. job/syscall = 'BSD
  1350. ]
  1351. all [
  1352. functions/:name/2 = 'syscall
  1353. job/target = 'ARM ;-- odd, but required for Linux/ARM syscalls
  1354. job/syscall = 'Linux
  1355. ]
  1356. ][
  1357. reverse args
  1358. ]
  1359. ]
  1360. comp-call: func [
  1361. name [word!] args [block!] /sub
  1362. /local list type res import? left right dup var-arity? saved? arg
  1363. ][
  1364. list: either issue? args/1 [ ;-- bypass type-checking for variable arity calls
  1365. args/2
  1366. ][
  1367. check-arguments-type name args
  1368. args
  1369. ]
  1370. order-args name list ;-- reorder argument according to cconv
  1371. import?: functions/:name/2 = 'import ;@@ syscalls don't seem to need special alignment??
  1372. if import? [emitter/target/emit-stack-align-prolog length? args]
  1373. type: functions/:name/2
  1374. either type <> 'op [
  1375. forall list [ ;-- push function's arguments on stack
  1376. if block? unbox list/1 [comp-expression list/1 yes] ;-- nested call
  1377. if type <> 'inline [
  1378. emitter/target/emit-argument list/1 type ;-- let target define how arguments are passed
  1379. ]
  1380. ]
  1381. ][ ;-- nested calls as op argument require special handling
  1382. if block? unbox list/1 [comp-expression list/1 yes] ;-- nested call
  1383. left: unbox list/1
  1384. right: unbox list/2
  1385. if saved?: all [block? left any [block? right path? right]][
  1386. emitter/target/emit-save-last ;-- optionally save left argument result
  1387. ]
  1388. if block? unbox list/2 [comp-expression list/2 yes] ;-- nested call
  1389. if saved? [emitter/target/emit-restore-last]
  1390. ]
  1391. res: emitter/target/emit-call name args to logic! sub
  1392. either res [
  1393. last-type: res
  1394. ][
  1395. set-last-type functions/:name/4 ;-- catch nested calls return type
  1396. ]
  1397. if import? [emitter/target/emit-stack-align-epilog length? args]
  1398. res
  1399. ]
  1400. comp-path-assign: func [
  1401. set-path [set-path!] expr casted [block! none!]
  1402. /local type new value
  1403. ][
  1404. unless get-variable-spec set-path/1 [
  1405. backtrack set-path
  1406. throw-error ["unknown path root variable:" set-path/1]
  1407. ]
  1408. type: resolve-path-type set-path ;-- check path validity
  1409. new: resolve-aliased get-type expr
  1410. if type <> any [casted new][
  1411. backtrack set-path
  1412. throw-error [
  1413. "type mismatch on setting path:" to path! set-path
  1414. "^/*** expected:" mold type
  1415. "^/*** found:" mold any [casted new]
  1416. ]
  1417. ]
  1418. value: unbox expr
  1419. if any [block? value path? value][value: <last>]
  1420. emitter/access-path set-path value
  1421. ]
  1422. comp-variable-assign: func [
  1423. set-word [set-word!] expr casted [block! none!]
  1424. /local name type new value
  1425. ][
  1426. name: to word! set-word
  1427. if find aliased-types name [
  1428. backtrack set-word
  1429. throw-error "name already used for as an alias definition"
  1430. ]
  1431. if not-initialized? name [
  1432. init-local name expr casted ;-- mark as initialized and infer type if required
  1433. ]
  1434. either type: get-variable-spec name [ ;-- test if known variable (local or global)
  1435. type: resolve-aliased type
  1436. new: resolve-aliased get-type expr
  1437. if type <> any [casted new][
  1438. backtrack set-word
  1439. throw-error [
  1440. "attempt to change type of variable:" name
  1441. "^/*** from:" mold type
  1442. "^/*** to:" mold any [casted new]
  1443. ]
  1444. ]
  1445. ][
  1446. unless zero? block-level [
  1447. backtrack set-word
  1448. throw-error "variable has to be initialized at root level"
  1449. ]
  1450. type: add-symbol name unbox expr casted ;-- if unknown add it to global context
  1451. ]
  1452. if none? type/1 [
  1453. backtrack set-word
  1454. throw-error ["unable to determine a type for:" name]
  1455. ]
  1456. value: unbox expr
  1457. if any [block? value path? value][value: <last>]
  1458. emitter/store name value type
  1459. ]
  1460. comp-expression: func [expr keep? [logic!] /local variable boxed casting new? type][
  1461. ;-- preprocessing expression
  1462. if all [block? expr find [set-word! set-path!] type?/word expr/1][
  1463. variable: expr/1
  1464. expr: expr/2 ;-- switch to assigned expression
  1465. if set-word? variable [
  1466. new?: not exists-variable? variable
  1467. ]
  1468. ]
  1469. if object? expr [ ;-- unbox type-casting object
  1470. if all [variable expr/action = 'null][
  1471. casting: cast-null variable
  1472. ]
  1473. boxed: expr
  1474. expr: cast expr
  1475. ]
  1476. ;-- emitting expression code
  1477. either block? expr [
  1478. type: comp-call expr/1 next expr ;-- function call case (recursive)
  1479. if type [last-type: type] ;-- set last-type if not already set
  1480. ][
  1481. unless any [
  1482. all [new? literal? unbox expr] ;-- if new variable, value will be store in data segment
  1483. all [set-path? variable literal? unbox expr] ;-- value loaded at lower level
  1484. tag? unbox expr
  1485. ][
  1486. emitter/target/emit-load expr ;-- emit code for single value
  1487. ]
  1488. last-type: resolve-expr-type expr
  1489. ]
  1490. ;-- postprocessing result
  1491. if boxed [
  1492. emitter/target/emit-casting boxed no ;-- insert runtime type casting if required
  1493. last-type: boxed/type
  1494. ]
  1495. if all [
  1496. any [keep? variable] ;-- if result needs to be stored
  1497. block? expr ;-- and if expr is a function call
  1498. last-type/1 = 'logic! ;-- which return type is logic!
  1499. ][
  1500. emitter/logic-to-integer expr/1 ;-- runtime logic! conversion before storing
  1501. ]
  1502. ;-- storing result if assignement required
  1503. if variable [
  1504. if all [boxed not casting][
  1505. casting: resolve-aliased boxed/type
  1506. ]
  1507. switch type?/word variable [
  1508. set-word! [comp-variable-assign variable expr casting]
  1509. set-path! [comp-path-assign variable expr casting]
  1510. ]
  1511. ]
  1512. ]
  1513. infix?: func [pos [block! paren!] /local specs][
  1514. all [
  1515. not tail? pos
  1516. word? pos/1
  1517. specs: select functions pos/1
  1518. find [op infix] specs/2
  1519. ]
  1520. ]
  1521. check-infix-operators: has [pos][
  1522. if infix? pc [exit] ;-- infix op already processed,
  1523. ;-- or used in prefix mode.
  1524. if infix? next pc [
  1525. either find [set-word! set-path! struct!] type?/word pc/1 [
  1526. throw-error "can't use infix operator here"
  1527. ][
  1528. pos: 0 ;-- relative index of next infix op
  1529. until [ ;-- search for all dependent infix op
  1530. pos: pos + 2 ;-- target next infix possible position
  1531. insert pc pc/:pos ;-- transform to prefix notation
  1532. remove at pc pos + 1
  1533. not infix? at pc pos + 2 ;-- exit when no more infix op found
  1534. ]
  1535. ]
  1536. ]
  1537. ]
  1538. fetch-expression: func [/final /keep /local expr pass][
  1539. check-infix-operators
  1540. if verbose >= 4 [print ["<<<" mold pc/1]]
  1541. pass: [also pc/1 pc: next pc]
  1542. if tail? pc [
  1543. pc: back pc
  1544. throw-error "missing argument"
  1545. ]
  1546. if job/debug? [store-dbg-lines]
  1547. expr: switch/default type?/word pc/1 [
  1548. set-word! [comp-assignment]
  1549. word! [comp-word]
  1550. get-word! [comp-get-word]
  1551. path! [comp-path]
  1552. set-path! [comp-assignment]
  1553. paren! [comp-block/only]
  1554. char! [do pass]
  1555. integer! [do pass]
  1556. string! [do pass]
  1557. ][
  1558. throw-error [
  1559. pick [
  1560. "compiler directives are not allowed in code blocks"
  1561. "datatype not allowed"
  1562. ] issue? pc/1
  1563. ]
  1564. ]
  1565. expr: reduce-logic-tests expr
  1566. if final [
  1567. if verbose >= 3 [?? expr]
  1568. unless find [none! tag!] type?/word expr [
  1569. comp-expression expr to logic! keep
  1570. ]
  1571. ]
  1572. expr
  1573. ]
  1574. comp-block: func [/final /only /local fetch expr][
  1575. fetch: [either final [fetch-expression/final][fetch-expression]]
  1576. block-level: block-level + 1
  1577. pc: fetch-into pc/1 [
  1578. either only [
  1579. expr: do fetch
  1580. unless tail? pc [
  1581. throw-error "more than one expression found in parentheses"
  1582. ]
  1583. ][
  1584. while [not tail? pc][
  1585. case [
  1586. paren? pc/1 [
  1587. unless infix? at pc 2 [raise-paren-error]
  1588. expr: do fetch
  1589. ]
  1590. all [word? pc/1 pc/1 = 'comment][pc: skip pc 2]
  1591. 'else [expr: do fetch]
  1592. ]
  1593. ]
  1594. ]
  1595. ]
  1596. block-level: block-level - 1
  1597. expr
  1598. ]
  1599. comp-dialect: has [expr][
  1600. block-level: 0
  1601. while [not tail? pc][
  1602. case [
  1603. issue? pc/1 [comp-directive]
  1604. all [
  1605. set-word? pc/1
  1606. find [func function] pc/2
  1607. ][
  1608. pc: next pc
  1609. fetch-func pc/-1 ;-- allow function declaration at root level only
  1610. ]
  1611. all [set-word? pc/1 pc/2 = 'alias][
  1612. pc: next pc
  1613. comp-alias ;-- allow alias declaration at root level only
  1614. ]
  1615. paren? pc/1 [
  1616. unless infix? at pc 2 [raise-paren-error]
  1617. expr: fetch-expression/final
  1618. ]
  1619. all [word? pc/1 pc/1 = 'comment][pc: skip pc 2]
  1620. 'else [expr: fetch-expression/final]
  1621. ]
  1622. emitter/target/on-root-level-entry
  1623. ]
  1624. expr
  1625. ]
  1626. comp-func-body: func [
  1627. name [word!] spec [block!] body [block!]
  1628. /local args-sz local-sz expr ret
  1629. ][
  1630. locals: spec
  1631. func-name: name
  1632. set [args-sz local-sz] emitter/enter name locals ;-- build function prolog
  1633. pc: body
  1634. expr: comp-dialect ;-- compile function's body
  1635. if ret: select spec return-def [
  1636. check-expected-type/ret name expr ret ;-- validate return value type
  1637. if all [
  1638. last-type/1 = 'logic!
  1639. block? expr
  1640. word? expr/1
  1641. ][
  1642. emitter/logic-to-integer expr/1 ;-- runtime logic! conversion before returning
  1643. ]
  1644. ]
  1645. emitter/leave name locals args-sz local-sz ;-- build function epilog
  1646. clear locals-init
  1647. locals: func-name: none
  1648. ]
  1649. comp-natives: does [
  1650. foreach [name spec body origin] natives [
  1651. if verbose >= 2 [
  1652. print [
  1653. "---------------------------------------^/"
  1654. "function:" name newline
  1655. "---------------------------------------"
  1656. ]
  1657. ]
  1658. script: origin
  1659. comp-func-body name spec body
  1660. ]
  1661. ]
  1662. comp-header: has [pos][
  1663. unless pc/1 = 'Red/System [
  1664. throw-error "source is not a Red/System program"
  1665. ]
  1666. pc: next pc
  1667. unless block? pc/1 [
  1668. throw-error "missing Red/System program header"
  1669. ]
  1670. unless parse pc/1 [any [pos: set-word! skip]][
  1671. throw-error ["invalid program header at:" mold pos]
  1672. ]
  1673. pc: next pc
  1674. ]
  1675. run: func [obj [object!] src [block!] file [file!] /no-header /runtime][
  1676. runtime: to logic! runtime
  1677. job: obj
  1678. pc: src
  1679. script: secure-clean-path file
  1680. unless no-header [comp-header]
  1681. emitter/target/on-global-prolog runtime
  1682. comp-dialect
  1683. case [
  1684. runtime [
  1685. emitter/target/on-global-epilog yes ;-- postpone epilog event after comp-runtime-epilog
  1686. ]
  1687. not job/runtime? [
  1688. emitter/target/on-global-epilog no
  1689. ]
  1690. ]
  1691. ]
  1692. finalize: does [
  1693. if verbose >= 2 [print "^/---^/Compiling native functions^/---"]
  1694. comp-natives
  1695. emitter/target/on-finalize
  1696. if verbose >= 2 [print ""]
  1697. emitter/reloc-native-calls
  1698. ]
  1699. ]
  1700. set-verbose-level: func [level [integer!]][
  1701. foreach ctx reduce [
  1702. self
  1703. loader
  1704. compiler
  1705. emitter
  1706. emitter/target
  1707. linker
  1708. ][
  1709. ctx/verbose: level
  1710. ]
  1711. ]
  1712. output-logs: does [
  1713. case/all [
  1714. verbose >= 1 [
  1715. print [
  1716. nl
  1717. "-- compiler/globals --" nl mold new-line/all/skip to-block compiler/globals yes 2 nl
  1718. "-- emitter/symbols --" nl mold emitter/symbols nl
  1719. ]
  1720. ]
  1721. verbose >= 2 [
  1722. print [
  1723. "-- compiler/functions --" nl mold compiler/functions nl
  1724. ]
  1725. ]
  1726. verbose >= 6 [
  1727. print [
  1728. "-- emitter/code-buf --" nl mold emitter/code-buf nl
  1729. "-- emitter/data-buf --" nl mold emitter/data-buf nl
  1730. "as-string:" nl mold as-string emitter/data-buf nl
  1731. ]
  1732. ]
  1733. ]
  1734. ]
  1735. comp-runtime-prolog: has [script][
  1736. script: secure-clean-path runtime-path/common.reds
  1737. compiler/run/runtime job loader/process script script
  1738. ]
  1739. comp-runtime-epilog: does [
  1740. compiler/comp-call '***-on-quit [0 0] ;-- call runtime exit handler
  1741. emitter/target/on-global-epilog no
  1742. ]
  1743. clean-up: does [
  1744. clear compiler/imports
  1745. clear compiler/natives
  1746. clear compiler/globals
  1747. clear compiler/aliased-types
  1748. clear compiler/user-functions
  1749. clear compiler/debug-lines/records
  1750. clear compiler/debug-lines/files
  1751. ]
  1752. make-job: func [opts [object!] file [file!] /local job][
  1753. job: construct/with third opts linker/job-class
  1754. unless job/build-basename [
  1755. file: last split-path file ;-- remove path
  1756. file: to-file first parse file "." ;-- remove extension
  1757. job/build-basename: file
  1758. ]
  1759. job
  1760. ]
  1761. dt: func [code [block!] /local t0][
  1762. t0: now/time/precise
  1763. do code
  1764. now/time/precise - t0
  1765. ]
  1766. options-class: context [
  1767. config-name: none ;-- Preconfigured compilation target ID
  1768. OS: none ;-- Operating System
  1769. OS-version: none ;-- OS version
  1770. link?: no ;-- yes = invoke the linker and finalize the job
  1771. debug?: no ;-- reserved for future use
  1772. build-prefix: %builds/ ;-- prefix to use for output file name (none: no prefix)
  1773. build-basename: none ;-- base name to use for output file name (none: derive from input name)
  1774. build-suffix: none ;-- suffix to use for output file name (none: derive from output type)
  1775. format: none ;-- file format
  1776. type: 'exe ;-- file type ('exe | 'dll | 'lib | 'obj)
  1777. target: 'IA-32 ;-- CPU target
  1778. verbosity: 0 ;-- logs verbosity level
  1779. sub-system: 'console ;-- 'GUI | 'console
  1780. runtime?: yes ;-- include Red/System runtime
  1781. use-natives?: no ;-- force use of native functions instead of C bindings
  1782. debug?: no ;-- emit debug information into binary
  1783. PIC?: no ;-- compile using Position Independent Code
  1784. base-address: none ;-- base image memory address
  1785. dynamic-linker: none ;-- ELF dynamic linker ("interpreter")
  1786. syscall: 'Linux ;-- syscalls convention: 'Linux | 'BSD
  1787. stack-align-16?: no ;-- yes => align stack to 16 bytes
  1788. literal-pool?: no ;-- yes => use pools to store literals, no => store them inlined (default: no)
  1789. ]
  1790. compile: func [
  1791. files [file! block!] ;-- source file or block of source files
  1792. /options
  1793. opts [object!]
  1794. /local
  1795. comp-time link-time err src
  1796. ][
  1797. comp-time: dt [
  1798. unless block? files [files: reduce [files]]
  1799. job: make-job opts last files ;-- last input filename is retained for output name
  1800. emitter/init opts/link? job
  1801. if opts/verbosity >= 10 [set-verbose-level opts/verbosity]
  1802. loader/init
  1803. if opts/runtime? [comp-runtime-prolog]
  1804. set-verbose-level opts/verbosity
  1805. foreach file files [compiler/run job loader/process file file]
  1806. set-verbose-level 0
  1807. if opts/runtime? [comp-runtime-epilog]
  1808. set-verbose-level opts/verbosity
  1809. compiler/finalize ;-- compile all functions
  1810. ]
  1811. if verbose >= 5 [
  1812. print [
  1813. "-- emitter/code-buf (empty addresses):"
  1814. nl mold emitter/code-buf nl
  1815. ]
  1816. ]
  1817. if opts/link? [
  1818. link-time: dt [
  1819. job/symbols: emitter/symbols
  1820. job/sections: compose/deep/only [
  1821. code [- (emitter/code-buf)]
  1822. data [- (emitter/data-buf)]
  1823. import [- - (compiler/imports)]
  1824. ]
  1825. if opts/debug? [
  1826. job/debug-info: reduce ['lines compiler/debug-lines]
  1827. ]
  1828. linker/build job
  1829. ]
  1830. ]
  1831. set-verbose-level opts/verbosity
  1832. output-logs
  1833. if opts/link? [clean-up]
  1834. reduce [comp-time link-time any [all [job/buffer length? job/buffer] 0]]
  1835. ]
  1836. ]