Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

338 linhas
11 KiB

  1. unit uengShaderFileParser;
  2. {$mode objfpc}{$H+}
  3. {$I uengShaderFile.inc}
  4. interface
  5. uses
  6. Classes, SysUtils,
  7. uengShaderFileTypes
  8. {$IFDEF USE_BITSPACE_UTILS}
  9. , uutlGenerics
  10. {$ELSE}
  11. , fgl
  12. {$ENDIF}
  13. ;
  14. type
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  16. TengTokenParameter = packed record
  17. Name: String;
  18. Quoted: Boolean;
  19. Line: Integer;
  20. Col: Integer;
  21. end;
  22. TengTokenParameterList = class(specialize TutlSimpleList<TengTokenParameter>)
  23. private
  24. fFilename: String;
  25. public
  26. property Filename: String read fFilename;
  27. constructor Create(const aFilename: String);
  28. end;
  29. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  30. TengParseArgs = class
  31. private
  32. fFilename: String;
  33. fCode: TStringList;
  34. fFlags: TengShaderFileLoadFlags;
  35. fLineLength: Integer;
  36. fLineCount: Integer;
  37. fCurrentLine: String;
  38. fCurrentChar: Char;
  39. fCol: Integer;
  40. fLine: Integer;
  41. procedure SetCol(const aValue: Integer);
  42. procedure SetLine(const aValue: Integer);
  43. function GetEndOfLine: Boolean;
  44. function GetEndOfFile: Boolean;
  45. function GetCode: TStrings;
  46. public
  47. property Code: TStrings read GetCode;
  48. property LineLength: Integer read fLineLength;
  49. property LineCount: Integer read fLineCount;
  50. property CurrentLine: String read fCurrentLine;
  51. property CurrentChar: Char read fCurrentChar;
  52. property EndOfLine: Boolean read GetEndOfLine;
  53. property EndOfFile: Boolean read GetEndOfFile;
  54. property Filename: String read fFilename;
  55. property Col: Integer read fCol write SetCol;
  56. property Line: Integer read fLine write SetLine;
  57. property Flags: TengShaderFileLoadFlags read fFlags write fFlags;
  58. procedure NextCol;
  59. procedure NextLine;
  60. function ExtractToken(const aParameters: TengTokenParameterList): Boolean;
  61. function GetTokenPreview(var aToken: String): Boolean;
  62. constructor Create(const aStream: TStream; const aFilename: String);
  63. destructor Destroy; override;
  64. end;
  65. implementation
  66. uses
  67. uengShaderFileConstants;
  68. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  69. //TengTokenParameterList////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  70. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  71. constructor TengTokenParameterList.Create(const aFilename: String);
  72. begin
  73. inherited Create(true);
  74. fFilename := aFilename;
  75. end;
  76. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  77. //TengParseArgs/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  78. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  79. procedure TengParseArgs.SetCol(const aValue: Integer);
  80. begin
  81. fCol := aValue;
  82. if not EndOfLine
  83. then fCurrentChar := fCurrentLine[fCol]
  84. else fCurrentChar := #0;
  85. end;
  86. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  87. procedure TengParseArgs.SetLine(const aValue: Integer);
  88. begin
  89. fLine := aValue;
  90. if not EndOfFile then begin
  91. fCurrentLine := fCode[fLine];
  92. fLineLength := Length(fCurrentLine);
  93. end else begin
  94. fCurrentLine := '';
  95. fLineLength := 0;
  96. end;
  97. Col := 1;
  98. end;
  99. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  100. function TengParseArgs.GetEndOfLine: Boolean;
  101. begin
  102. result := (fCol > fLineLength)
  103. end;
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. function TengParseArgs.GetEndOfFile: Boolean;
  106. begin
  107. result := (fLine >= fLineCount);
  108. end;
  109. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. procedure TengParseArgs.NextCol;
  111. begin
  112. Col := Col + 1;
  113. end;
  114. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  115. procedure TengParseArgs.NextLine;
  116. begin
  117. Line := Line + 1;
  118. end;
  119. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  120. function TengParseArgs.ExtractToken(const aParameters: TengTokenParameterList): Boolean;
  121. type
  122. TCharType = (
  123. ctUnknown,
  124. ctValidTokenChar,
  125. ctInvalidTokenChar
  126. );
  127. TParseFlag = (
  128. pfQuote,
  129. pfInToken,
  130. pfIsComment,
  131. pfCommentTokenAdded
  132. );
  133. TParseFlags = set of TParseFlag;
  134. var
  135. pf: TParseFlags;
  136. ct: TCharType;
  137. s: String;
  138. pLine, pCol, oldLine, oldCol: Integer;
  139. procedure AddPart(const aTrimAndCheck: Boolean = true);
  140. var
  141. len: Integer;
  142. p: TengTokenParameter;
  143. begin
  144. if aTrimAndCheck then
  145. s := Trim(s);
  146. if not aTrimAndCheck or (s <> '') then begin
  147. len := Length(s);
  148. if aTrimAndCheck and
  149. ( (s[1] = TOKEN_CHAR_QUOTE) and
  150. (s[len] = TOKEN_CHAR_QUOTE)) then
  151. begin
  152. if not (s[1] = TOKEN_CHAR_QUOTE) then
  153. raise EengShaderPart.Create('missing leading quote char', Line, Col, Filename);
  154. if not (s[len] = TOKEN_CHAR_QUOTE) then
  155. raise EengShaderPart.Create('missing trailing quote char', Line, Col, Filename);
  156. delete(s, len, 1);
  157. delete(s, 1, 1);
  158. p.Quoted := true;
  159. end else
  160. p.Quoted := false;
  161. p.Name := s;
  162. p.Line := pLine;
  163. p.Col := pCol;
  164. aParameters.Add(p);
  165. end;
  166. s := '';
  167. pLine := Line;
  168. pCol := Col;
  169. ct := ctUnknown;
  170. end;
  171. begin
  172. result := false;
  173. aParameters.Clear;
  174. if (CurrentChar <> TOKEN_CHAR_BEGIN) or
  175. (Col + 1 > LineLength) or
  176. ( (CurrentLine[Col+1] <> TOKEN_CHAR_IDENT) and
  177. (CurrentLine[Col+1] <> TOKEN_CHAR_COMMENT))
  178. then
  179. exit;
  180. result := true;
  181. s := '';
  182. ct := ctUnknown;
  183. oldLine := Line;
  184. oldCol := Col;
  185. pf := [];
  186. if (CurrentLine[Col+1] = TOKEN_CHAR_COMMENT) then
  187. Include(pf, pfIsComment);
  188. AddPart; // initialize
  189. while not EndOfFile do begin
  190. while not EndOfLine do begin
  191. case CurrentChar of
  192. TOKEN_CHAR_BEGIN: begin
  193. if (pfQuote in pf) then
  194. s := s + CurrentChar
  195. else if not (pfInToken in pf) then
  196. Include(pf, pfInToken)
  197. else
  198. EengShaderPart.Create('invalid char in token', Line, Col, Filename);
  199. end;
  200. TOKEN_CHAR_END: begin
  201. if not (pfQuote in pf) then begin
  202. AddPart(not (pfIsComment in pf));
  203. NextCol;
  204. if (aParameters.Count <= 0) or (aParameters[0].Name = TOKEN_CHAR_IDENT) then
  205. raise EengShaderPart.Create('empty token', Line, Col, Filename);
  206. exit;
  207. end else
  208. s := s + CurrentChar;
  209. end;
  210. TOKEN_CHAR_QUOTE: begin
  211. if not (pfQuote in pf) and not (pfIsComment in pf) then
  212. AddPart;
  213. s := s + CurrentChar;
  214. if (pfQuote in pf)
  215. then exclude(pf, pfQuote)
  216. else include(pf, pfQuote);
  217. if not (pfQuote in pf) and not (pfIsComment in pf) then
  218. AddPart;
  219. end;
  220. TOKEN_CHAR_COMMENT: begin
  221. s := s + CurrentChar;
  222. if (pfIsComment in pf) and not (pfCommentTokenAdded in pf) then begin
  223. include(pf, pfCommentTokenAdded);
  224. AddPart(false);
  225. end;
  226. end;
  227. else
  228. if not (pfQuote in pf) and not (pfIsComment in pf) then begin
  229. if (CurrentChar in TOKEN_SPLIT_CHARS) then begin
  230. AddPart;
  231. end else if (CurrentChar in VALID_TOKEN_CHARS) then begin
  232. if (ct <> ctValidTokenChar) then
  233. AddPart;
  234. ct := ctValidTokenChar;
  235. end else begin
  236. if (ct <> ctInvalidTokenChar) then
  237. AddPart;
  238. ct := ctInvalidTokenChar;
  239. end;
  240. end;
  241. s := s + CurrentChar;
  242. end;
  243. NextCol;
  244. end;
  245. s := s + sLineBreak;
  246. NextLine;
  247. end;
  248. raise EengShaderPart.Create('incomplete token', oldLine, oldCol, Filename);
  249. end;
  250. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  251. function TengParseArgs.GetCode: TStrings;
  252. begin
  253. result := fCode;
  254. end;
  255. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  256. function TengParseArgs.GetTokenPreview(var aToken: String): Boolean;
  257. var
  258. c: Integer;
  259. begin
  260. result := false;
  261. if (CurrentChar <> TOKEN_CHAR_BEGIN) or
  262. (Col >= LineLength) or
  263. not (CurrentLine[Col + 1] in [TOKEN_CHAR_IDENT, TOKEN_CHAR_COMMENT])
  264. then exit;
  265. result := true;
  266. aToken := CurrentChar;
  267. if (aToken = TOKEN_CHAR_COMMANT_END) then
  268. exit;
  269. c := Col + 1;
  270. case CurrentLine[c] of
  271. TOKEN_CHAR_IDENT: begin
  272. aToken := '';
  273. repeat
  274. aToken := aToken + CurrentLine[c];
  275. inc(c);
  276. until (c > LineLength) or not (CurrentLine[c] in VALID_TOKEN_CHARS);
  277. aToken := Trim(aToken);
  278. if (aToken = TOKEN_CHAR_IDENT) then
  279. raise EengInvalidToken.Create('empty token', Line, Col, Filename);
  280. end;
  281. TOKEN_CHAR_COMMENT:
  282. aToken := TOKEN_CHAR_COMMENT;
  283. end;
  284. end;
  285. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  286. constructor TengParseArgs.Create(const aStream: TStream; const aFilename: String);
  287. begin
  288. inherited Create;
  289. fFilename := aFilename;
  290. fCode := TStringList.Create;
  291. fCode.LoadFromStream(aStream);
  292. fLineCount := fCode.Count;
  293. Line := 0;
  294. end;
  295. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  296. destructor TengParseArgs.Destroy;
  297. begin
  298. FreeAndNil(fCode);
  299. inherited Destroy;
  300. end;
  301. end.