parser.inc 9.6 KB

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