parser.inc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2007 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {****************************************************************************}
  11. {* TParser *}
  12. {****************************************************************************}
  13. const
  14. ParseBufSize = 4096;
  15. LastSpecialToken = 5;
  16. TokNames : array[0..LastSpecialToken] of string =
  17. (
  18. 'EOF',
  19. 'Symbol',
  20. 'String',
  21. 'Integer',
  22. 'Float',
  23. 'WideString'
  24. );
  25. function TParser.GetTokenName(aTok: char): string;
  26. begin
  27. if ord(aTok) <= LastSpecialToken then
  28. Result:=TokNames[ord(aTok)]
  29. else Result:=aTok;
  30. end;
  31. procedure TParser.LoadBuffer;
  32. var toread : integer;
  33. begin
  34. toread:=fStream.Size-fStream.Position;
  35. if toread>ParseBufSize then toread:=ParseBufSize;
  36. if toread=0 then
  37. begin
  38. fEofReached:=true;
  39. exit;
  40. end;
  41. fStream.ReadBuffer(fBuf[0],toread);
  42. fBuf[toread]:=#0;
  43. inc(fDeltaPos,fPos);
  44. fPos:=0;
  45. fBufLen:=toread;
  46. end;
  47. procedure TParser.CheckLoadBuffer; inline;
  48. begin
  49. if fBuf[fPos]=#0 then LoadBuffer;
  50. end;
  51. procedure TParser.ProcessChar; inline;
  52. begin
  53. fLastTokenStr:=fLastTokenStr+fBuf[fPos];
  54. inc(fPos);
  55. CheckLoadBuffer;
  56. end;
  57. function TParser.IsNumber: boolean; inline;
  58. begin
  59. Result:=fBuf[fPos] in ['0'..'9'];
  60. end;
  61. function TParser.IsHexNum: boolean; inline;
  62. begin
  63. Result:=fBuf[fPos] in ['0'..'9','A'..'F','a'..'f'];
  64. end;
  65. function TParser.IsAlpha: boolean; inline;
  66. begin
  67. Result:=fBuf[fPos] in ['_','A'..'Z','a'..'z'];
  68. end;
  69. function TParser.IsAlphaNum: boolean; inline;
  70. begin
  71. Result:=IsAlpha or IsNumber;
  72. end;
  73. function TParser.GetHexValue(c: char): byte; inline;
  74. begin
  75. case c of
  76. '0'..'9' : Result:=ord(c)-$30;
  77. 'A'..'F' : Result:=ord(c)-$37; //-$41+$0A
  78. 'a'..'f' : Result:=ord(c)-$57; //-$61+$0A
  79. end;
  80. end;
  81. function TParser.GetAlphaNum: string;
  82. begin
  83. if not IsAlpha then
  84. ErrorFmt(SParExpected,[GetTokenName(toSymbol)]);
  85. Result:='';
  86. while IsAlphaNum do
  87. begin
  88. Result:=Result+fBuf[fPos];
  89. inc(fPos);
  90. CheckLoadBuffer;
  91. end;
  92. end;
  93. procedure TParser.HandleNewLine;
  94. begin
  95. if fBuf[fPos]=#13 then //CR
  96. begin
  97. inc(fPos);
  98. CheckLoadBuffer;
  99. if fBuf[fPos]=#10 then inc(fPos); //CR LF
  100. end
  101. else inc(fPos); //LF
  102. inc(fSourceLine);
  103. fDeltaPos:=-(fPos-1);
  104. end;
  105. procedure TParser.SkipSpaces;
  106. begin
  107. while fBuf[fPos] in [' ',#9] do
  108. inc(fPos);
  109. end;
  110. procedure TParser.SkipWhitespace;
  111. begin
  112. while true do
  113. begin
  114. CheckLoadBuffer;
  115. case fBuf[fPos] of
  116. ' ',#9 : SkipSpaces;
  117. #10,#13 : HandleNewLine
  118. else break;
  119. end;
  120. end;
  121. end;
  122. procedure TParser.HandleEof;
  123. begin
  124. fToken:=toEOF;
  125. fLastTokenStr:='';
  126. end;
  127. procedure TParser.HandleAlphaNum;
  128. begin
  129. fLastTokenStr:=GetAlphaNum;
  130. fToken:=toSymbol;
  131. end;
  132. procedure TParser.HandleNumber;
  133. type
  134. floatPunct = (fpDot,fpE);
  135. floatPuncts = set of floatPunct;
  136. var
  137. allowed : floatPuncts;
  138. begin
  139. fLastTokenStr:='';
  140. while IsNumber do
  141. ProcessChar;
  142. fToken:=toInteger;
  143. if (fBuf[fPos] in ['.','e','E']) then
  144. begin
  145. fToken:=toFloat;
  146. allowed:=[fpDot,fpE];
  147. while (fBuf[fPos] in ['.','e','E','0'..'9']) do
  148. begin
  149. case fBuf[fPos] of
  150. '.' : if fpDot in allowed then Exclude(allowed,fpDot) else break;
  151. 'E','e' : if fpE in allowed then
  152. begin
  153. allowed:=[];
  154. ProcessChar;
  155. if (fBuf[fPos] in ['+','-']) then ProcessChar;
  156. if not (fBuf[fPos] in ['0'..'9']) then
  157. ErrorFmt(SParInvalidFloat,[fLastTokenStr+fBuf[fPos]]);
  158. end
  159. else break;
  160. end;
  161. ProcessChar;
  162. end;
  163. end;
  164. if (fBuf[fPos] in ['s','S','d','D','c','C']) then //single, date, currency
  165. begin
  166. fFloatType:=fBuf[fPos];
  167. inc(fPos);
  168. fToken:=toFloat;
  169. end
  170. else fFloatType:=#0;
  171. end;
  172. procedure TParser.HandleHexNumber;
  173. var valid : boolean;
  174. begin
  175. fLastTokenStr:='$';
  176. inc(fPos);
  177. CheckLoadBuffer;
  178. valid:=false;
  179. while IsHexNum do
  180. begin
  181. valid:=true;
  182. ProcessChar;
  183. end;
  184. if not valid then
  185. ErrorFmt(SParInvalidInteger,[fLastTokenStr]);
  186. fToken:=toInteger;
  187. end;
  188. function TParser.HandleQuotedString: string;
  189. begin
  190. Result:='';
  191. inc(fPos);
  192. CheckLoadBuffer;
  193. while true do
  194. begin
  195. case fBuf[fPos] of
  196. #0 : ErrorStr(SParUnterminatedString);
  197. #13,#10 : ErrorStr(SParUnterminatedString);
  198. '''' : begin
  199. inc(fPos);
  200. CheckLoadBuffer;
  201. if fBuf[fPos]<>'''' then exit;
  202. end;
  203. end;
  204. Result:=Result+fBuf[fPos];
  205. inc(fPos);
  206. CheckLoadBuffer;
  207. end;
  208. end;
  209. function TParser.HandleDecimalString(var ascii : boolean): widestring;
  210. var i : integer;
  211. begin
  212. Result:='';
  213. inc(fPos);
  214. CheckLoadBuffer;
  215. while IsNumber do
  216. begin
  217. Result:=Result+fBuf[fPos];
  218. inc(fPos);
  219. CheckLoadBuffer;
  220. end;
  221. if not TryStrToInt(Result,i) then
  222. i:=0;
  223. if i>127 then ascii:=false;
  224. setlength(Result,1);
  225. Result[1]:=widechar(word(i));
  226. end;
  227. procedure TParser.HandleString;
  228. var ascii : boolean;
  229. begin
  230. fLastTokenWStr:='';
  231. ascii:=true;
  232. while true do
  233. case fBuf[fPos] of
  234. '''' : fLastTokenWStr:=fLastTokenWStr+HandleQuotedString;
  235. '#' : fLastTokenWStr:=fLastTokenWStr+HandleDecimalString(ascii)
  236. else break;
  237. end;
  238. if ascii then
  239. fToken:=toString
  240. else
  241. fToken:=toWString;
  242. fLastTokenStr:=fLastTokenWStr;
  243. end;
  244. procedure TParser.HandleMinus;
  245. begin
  246. inc(fPos);
  247. CheckLoadBuffer;
  248. if IsNumber then
  249. begin
  250. HandleNumber;
  251. fLastTokenStr:='-'+fLastTokenStr;
  252. end
  253. else
  254. begin
  255. fToken:='-';
  256. fLastTokenStr:=fToken;
  257. end;
  258. end;
  259. procedure TParser.HandleUnknown;
  260. begin
  261. fToken:=fBuf[fPos];
  262. fLastTokenStr:=fToken;
  263. inc(fPos);
  264. end;
  265. constructor TParser.Create(Stream: TStream);
  266. begin
  267. fStream:=Stream;
  268. fBuf:=GetMem(ParseBufSize+1);
  269. fBufLen:=0;
  270. fPos:=0;
  271. fDeltaPos:=1;
  272. fSourceLine:=1;
  273. fEofReached:=false;
  274. fLastTokenStr:='';
  275. fLastTokenWStr:='';
  276. fFloatType:=#0;
  277. fToken:=#0;
  278. LoadBuffer;
  279. NextToken;
  280. end;
  281. destructor TParser.Destroy;
  282. begin
  283. fStream.Position:=SourcePos;
  284. FreeMem(fBuf);
  285. end;
  286. procedure TParser.CheckToken(T: Char);
  287. begin
  288. if fToken<>T then
  289. ErrorFmt(SParWrongTokenType,[GetTokenName(T),GetTokenName(fToken)]);
  290. end;
  291. procedure TParser.CheckTokenSymbol(const S: string);
  292. begin
  293. CheckToken(toSymbol);
  294. if CompareText(fLastTokenStr,S)<>0 then
  295. ErrorFmt(SParWrongTokenSymbol,[s,fLastTokenStr]);
  296. end;
  297. procedure TParser.Error(const Ident: string);
  298. begin
  299. ErrorStr(Ident);
  300. end;
  301. procedure TParser.ErrorFmt(const Ident: string; const Args: array of const);
  302. begin
  303. ErrorStr(Format(Ident,Args));
  304. end;
  305. procedure TParser.ErrorStr(const Message: string);
  306. begin
  307. raise EParserError.CreateFmt(Message+SParLocInfo,[SourceLine,fPos+fDeltaPos,SourcePos]);
  308. end;
  309. procedure TParser.HexToBinary(Stream: TStream);
  310. var outbuf : array[0..ParseBufSize-1] of byte;
  311. b : byte;
  312. i : integer;
  313. begin
  314. i:=0;
  315. SkipWhitespace;
  316. while IsHexNum do
  317. begin
  318. b:=(GetHexValue(fBuf[fPos]) shl 4);
  319. inc(fPos);
  320. CheckLoadBuffer;
  321. if not IsHexNum then
  322. Error(SParUnterminatedBinValue);
  323. b:=b or GetHexValue(fBuf[fPos]);
  324. inc(fPos);
  325. outbuf[i]:=b;
  326. inc(i);
  327. if i>=ParseBufSize then
  328. begin
  329. Stream.WriteBuffer(outbuf[0],i);
  330. i:=0;
  331. end;
  332. SkipWhitespace;
  333. end;
  334. if i>0 then
  335. Stream.WriteBuffer(outbuf[0],i);
  336. NextToken;
  337. end;
  338. function TParser.NextToken: Char;
  339. begin
  340. SkipWhiteSpace;
  341. if fEofReached then
  342. HandleEof
  343. else
  344. case fBuf[fPos] of
  345. '_','A'..'Z','a'..'z' : HandleAlphaNum;
  346. '$' : HandleHexNumber;
  347. '-' : HandleMinus;
  348. '0'..'9' : HandleNumber;
  349. '''','#' : HandleString
  350. else
  351. HandleUnknown;
  352. end;
  353. Result:=fToken;
  354. end;
  355. function TParser.SourcePos: Longint;
  356. begin
  357. Result:=fStream.Position-fBufLen+fPos;
  358. end;
  359. function TParser.TokenComponentIdent: string;
  360. begin
  361. if fToken<>toSymbol then
  362. ErrorFmt(SParExpected,[GetTokenName(toSymbol)]);
  363. CheckLoadBuffer;
  364. while fBuf[fPos]='.' do
  365. begin
  366. ProcessChar;
  367. fLastTokenStr:=fLastTokenStr+GetAlphaNum;
  368. end;
  369. Result:=fLastTokenStr;
  370. end;
  371. {$ifndef FPUNONE}
  372. Function TParser.TokenFloat: Extended;
  373. var errcode : word;
  374. begin
  375. Val(fLastTokenStr,Result,errcode);
  376. if errcode<>0 then
  377. ErrorFmt(SParInvalidFloat,[fLastTokenStr]);
  378. end;
  379. {$endif}
  380. Function TParser.TokenInt: Int64;
  381. begin
  382. if not TryStrToInt64(fLastTokenStr,Result) then
  383. Result:=Int64(StrToQWord(fLastTokenStr)); //second chance for malformed files
  384. end;
  385. function TParser.TokenString: string;
  386. begin
  387. case fToken of
  388. toWString : Result:=fLastTokenWStr;
  389. toFloat : if fFloatType<>#0 then
  390. Result:=fLastTokenStr+fFloatType
  391. else Result:=fLastTokenStr
  392. else
  393. Result:=fLastTokenStr;
  394. end;
  395. end;
  396. function TParser.TokenWideString: WideString;
  397. begin
  398. if fToken=toWString then
  399. Result:=fLastTokenWStr
  400. else
  401. Result:=fLastTokenStr;
  402. end;
  403. function TParser.TokenSymbolIs(const S: string): Boolean;
  404. begin
  405. Result:=(fToken=toSymbol) and (CompareText(fLastTokenStr,S)=0);
  406. end;