You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
11 KiB

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