CompilerSource.mod 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. IMPLEMENTATION MODULE CompilerSource ;
  2. (* Source-buffer access and {$I file} expansion. The parser still sees one
  3. flat TextBuf; this module owns the cursor, include state and the include
  4. pre-pass. *)
  5. FROM SYSTEM IMPORT BYTE, ADR ;
  6. FROM TextBuf IMPORT TextLimit, Length, CharAt, DeleteFromTo, InsertStr ;
  7. FROM Posix IMPORT open, read, close ;
  8. FROM CompilerShared IMPORT srcPos, Err, OK, EMemOvf, EFileNF, EInclNest ;
  9. VAR
  10. srcName : ARRAY [0..255] OF CHAR ;
  11. inclName : ARRAY [0..127] OF CHAR ;
  12. inclPath : ARRAY [0..383] OF CHAR ;
  13. incBuf : ARRAY [0..TextLimit] OF CHAR ;
  14. PROCEDURE CurCh () : CHAR ;
  15. BEGIN
  16. IF srcPos >= srcLen THEN
  17. RETURN 0C
  18. END ;
  19. RETURN CharAt (srcPos)
  20. END CurCh ;
  21. PROCEDURE GetCh () : CHAR ;
  22. VAR ch : CHAR ;
  23. BEGIN
  24. ch := CurCh () ;
  25. IF srcPos < srcLen THEN
  26. INC (srcPos)
  27. END ;
  28. RETURN ch
  29. END GetCh ;
  30. PROCEDURE PeekAhead (k : CARDINAL) : CHAR ;
  31. BEGIN
  32. IF srcPos + k >= srcLen THEN
  33. RETURN 0C
  34. END ;
  35. RETURN CharAt (srcPos + k)
  36. END PeekAhead ;
  37. PROCEDURE Upper (ch : CHAR) : CHAR ;
  38. BEGIN
  39. IF (ch >= 'a') AND (ch <= 'z') THEN
  40. RETURN CHR (ORD (ch) - ORD ('a') + ORD ('A'))
  41. END ;
  42. RETURN ch
  43. END Upper ;
  44. PROCEDURE SetSourcePath (path : ARRAY OF CHAR) ;
  45. (* remember the path the buffer was loaded from; MakeIncludePath keeps only its
  46. directory *)
  47. VAR i : CARDINAL ;
  48. BEGIN
  49. i := 0 ;
  50. WHILE (i < 255) AND (i <= HIGH (path)) AND (path [i] # 0C) DO
  51. srcName [i] := path [i] ;
  52. INC (i)
  53. END ;
  54. srcName [i] := 0C
  55. END SetSourcePath ;
  56. PROCEDURE ReadWholeFile (path : ARRAY OF CHAR ; VAR n : CARDINAL) : BOOLEAN ;
  57. (* Read path into incBuf with the same CR/LF normalisation as LoadWorkFile,
  58. and report its length in n. FALSE if it could not be opened. ^Z ends the
  59. text, as it does for every other reader here. *)
  60. VAR fd : INTEGER ;
  61. k : LONGINT ;
  62. b : BYTE ;
  63. ch : CHAR ;
  64. prevCR, emit : BOOLEAN ;
  65. BEGIN
  66. n := 0 ;
  67. fd := open (ADR (path), 0, 0) ;
  68. IF fd < 0 THEN
  69. RETURN FALSE
  70. END ;
  71. prevCR := FALSE ;
  72. LOOP
  73. k := read (fd, ADR (b), 1) ;
  74. IF k # 1 THEN EXIT END ;
  75. IF b = 26 THEN EXIT END ; (* ^Z *)
  76. emit := TRUE ;
  77. IF b = 10 THEN
  78. IF prevCR THEN
  79. emit := FALSE
  80. ELSE
  81. ch := CHR (13) (* lone LF -> CR *)
  82. END ;
  83. prevCR := FALSE
  84. ELSIF b = 13 THEN
  85. ch := CHR (13) ;
  86. prevCR := TRUE
  87. ELSE
  88. ch := CHR (ORD (b)) ;
  89. prevCR := FALSE
  90. END ;
  91. IF emit AND (n <= TextLimit) THEN
  92. incBuf [n] := ch ;
  93. INC (n)
  94. END
  95. END ;
  96. k := close (fd) ;
  97. RETURN TRUE
  98. END ReadWholeFile ;
  99. PROCEDURE MakeIncludePath () ;
  100. (* inclPath := directory (srcName) + inclName, defaulting the extension to
  101. .PAS when the name has none (TPSRC10 kpasext). *)
  102. VAR i, j, last : CARDINAL ; dot : BOOLEAN ;
  103. BEGIN
  104. dot := FALSE ;
  105. i := 0 ;
  106. WHILE (i < 127) AND (inclName [i] # 0C) DO
  107. IF inclName [i] = '.' THEN dot := TRUE END ;
  108. INC (i)
  109. END ;
  110. IF (NOT dot) AND (i <= 123) THEN
  111. inclName [i] := '.' ;
  112. inclName [i + 1] := 'P' ;
  113. inclName [i + 2] := 'A' ;
  114. inclName [i + 3] := 'S' ;
  115. inclName [i + 4] := 0C
  116. END ;
  117. last := 0 ;
  118. i := 0 ;
  119. WHILE (i < 256) AND (srcName [i] # 0C) DO
  120. IF srcName [i] = '/' THEN last := i + 1 END ;
  121. INC (i)
  122. END ;
  123. j := 0 ;
  124. WHILE j < last DO
  125. inclPath [j] := srcName [j] ;
  126. INC (j)
  127. END ;
  128. i := 0 ;
  129. WHILE (i < 127) AND (inclName [i] # 0C) DO
  130. inclPath [j] := inclName [i] ;
  131. INC (j) ;
  132. INC (i)
  133. END ;
  134. inclPath [j] := 0C
  135. END MakeIncludePath ;
  136. PROCEDURE ExpandIncludes () ;
  137. (* Replace every {$I name} directive in the buffer with the named file's text,
  138. BEFORE parsing.
  139. This is a pre-pass, and it has to be. Skip is called by lookahead routines
  140. (DeclaresProc, PeekKw, KwAhead) that SAVE AND RESTORE srcPos; an include
  141. performed inside Skip would run once during the lookahead and once for real,
  142. and the second pass would see the first splice's text and report error 96.
  143. A splice also grows the buffer, which a rewind cannot undo. So the whole
  144. expansion is done here, in one forward scan with no lookahead, and the
  145. parser only ever sees the finished text.
  146. The scan has to recognise exactly what the lexer does, or it would expand a
  147. directive that the lexer would have read as a string or a comment: a quoted
  148. literal is skipped, then star-paren comments (which is why {$I} written
  149. inside one is inert - t56_include.inc relies on that), then brace comments,
  150. and only then a brace directive.
  151. Divergence from the original: TP3 STREAMS the include file line by line and
  152. switches back to the outer file at EOF (TPSRC10 fileline/getincl/fileeof);
  153. we hold the whole source in TextBuf and address it by index, so the include
  154. is spliced in place. A nested {$I} therefore cannot switch back on its own
  155. - which TP3 also refuses, and that is the error 96 reported here. *)
  156. VAR i, d0, d1, k, ni, n : CARDINAL ;
  157. c, letter : CHAR ;
  158. isInc : BOOLEAN ;
  159. BEGIN
  160. i := 0 ;
  161. inclActive := FALSE ;
  162. inclEnd := 0 ;
  163. WHILE (i < Length ()) AND OK () DO
  164. c := CharAt (i) ;
  165. IF c = "'" THEN
  166. INC (i) ;
  167. WHILE (i < Length ()) AND (CharAt (i) # "'") DO INC (i) END ;
  168. IF i < Length () THEN INC (i) END
  169. ELSIF (c = '(') AND (i + 1 < Length ()) AND (CharAt (i + 1) = '*') THEN
  170. INC (i, 2) ;
  171. WHILE (i < Length ()) AND NOT ((CharAt (i) = '*')
  172. AND (i + 1 < Length ()) AND (CharAt (i + 1) = ')')) DO
  173. INC (i)
  174. END ;
  175. IF i < Length () THEN INC (i, 2) END
  176. ELSIF (c = '{') AND (i + 1 < Length ()) AND (CharAt (i + 1) = '$') THEN
  177. d0 := i ;
  178. d1 := i + 2 ;
  179. WHILE (d1 < Length ()) AND (CharAt (d1) # '}') DO INC (d1) END ;
  180. IF i + 2 < Length () THEN
  181. letter := Upper (CharAt (i + 2))
  182. ELSE
  183. letter := 0C
  184. END ;
  185. isInc := FALSE ;
  186. IF letter = 'I' THEN
  187. k := i + 3 ;
  188. WHILE (k < d1) AND ((CharAt (k) = ' ') OR (CharAt (k) = CHR (9))) DO
  189. INC (k)
  190. END ;
  191. IF (k >= d1) OR ((CharAt (k) # '+') AND (CharAt (k) # '-')) THEN
  192. isInc := TRUE
  193. END
  194. END ;
  195. IF isInc THEN
  196. IF inclActive AND (d0 < inclEnd) THEN
  197. srcPos := d0 ;
  198. Err (EInclNest) ; (* 96 *)
  199. RETURN
  200. END ;
  201. ni := 0 ;
  202. WHILE (k < d1) AND (ni < 127) DO
  203. inclName [ni] := CharAt (k) ;
  204. INC (k) ;
  205. INC (ni)
  206. END ;
  207. WHILE (ni > 0) AND ((inclName [ni - 1] = ' ')
  208. OR (inclName [ni - 1] = CHR (9))) DO
  209. DEC (ni)
  210. END ;
  211. inclName [ni] := 0C ;
  212. MakeIncludePath () ;
  213. IF NOT ReadWholeFile (inclPath, n) THEN
  214. srcPos := d0 ;
  215. Err (EFileNF) ; (* 90 *)
  216. RETURN
  217. END ;
  218. DeleteFromTo (d0, d1 - d0 + 1) ;
  219. IF NOT InsertStr (d0, incBuf, n) THEN
  220. srcPos := d0 ;
  221. Err (EMemOvf) ;
  222. RETURN
  223. END ;
  224. inclEnd := d0 + n ;
  225. inclActive := TRUE ;
  226. i := d0 (* scan the new text too *)
  227. ELSE
  228. i := d1 + 1 (* ordinary directive: leave it *)
  229. END
  230. ELSIF c = '{' THEN
  231. INC (i) ;
  232. WHILE (i < Length ()) AND (CharAt (i) # '}') DO INC (i) END ;
  233. IF i < Length () THEN INC (i) END
  234. ELSE
  235. INC (i)
  236. END
  237. END ;
  238. srcLen := Length () ;
  239. srcPos := 0
  240. END ExpandIncludes ;
  241. END CompilerSource.