home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / handson / archive / Issue160 / files / NewStreamIn / strin.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1999-10-18  |  14.3 KB  |  453 lines

  1. unit{a test comment with no spaces around it}strin;
  2.  
  3. (* PC PLUS Sample Delphi syntax-colouring application.
  4.  
  5.    Illustrates a simple way to read a plain text file containing program code,
  6.    colour it according to its syntax, by adding RTF codes as necessary,
  7.    then fill a RichEdit control with the coloured code.
  8.  
  9.    Syntax recognised:
  10.    - Delphi keywords
  11.    - Strings
  12.    - Curly-brace comments
  13.    - Starred comments
  14.    - Single-line comments
  15.  
  16.    Deficiencies:
  17.    - Limited error-handling
  18.      note: this can be improved by adding exception handling when attempting
  19.            to read and write from streams and by testing the 'status'
  20.            parameter after every read-write operation.
  21.    - Could be optimised
  22.      I have not gone to any lengths to fine-tune my code for efficiency. The
  23.      speed of file loading is perfectly adequate with files up to 500K or so.
  24.      With files in excess of a Megabyte, there is a noticable delay - but only
  25.      of a few seconds (on a 333MHz PC), which seems quite reasonable. All the
  26.      same, there are places in the code where the efficiency could certainly
  27.      be improved.
  28.  
  29.    Author: Huw Collingbourne.
  30.  
  31. *)
  32.  
  33. // The following comments have been added to test colour-coding when
  34. // loading this source file.
  35. { This is a
  36.   2-line comment}
  37. interface
  38.       { 'a string'  (*a starred
  39.          comment *)  inside a standard one  }
  40. (* This is a starred comment *)
  41. (* {a standard comment} inside a starred one        *)
  42. // { standard comment after line comment }
  43. { // line comment in standard comment }
  44.  
  45. uses
  46.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  47.   StdCtrls, ComCtrls, ExtCtrls;
  48.  
  49. const
  50.      // possible values of status parameter
  51.      SUCCESS = 1;
  52.      NOERROR = 2;
  53.      READERROR = 3;
  54.  
  55. type
  56.  
  57.   chararray = array[0..255] of char;
  58.      // ReadStatus defines type of the status parameter
  59.   ReadStatus = SUCCESS..READERROR;
  60.  
  61.   TForm1 = class(TForm)
  62.     RichEdit1: TRichEdit;
  63.     Panel1: TPanel;
  64.     LoadFileBtn: TButton;
  65.     PlainTextRBtn: TCheckBox;
  66.     procedure LoadFileBtnClick(Sender: TObject);
  67.     procedure FormCreate(Sender: TObject);
  68.     procedure PlainTextRBtnClick(Sender: TObject);
  69.   private
  70.     { Private declarations }
  71.   public
  72.     { Public declarations }
  73.     function KeyWord( token : string ) : boolean;
  74.     procedure WriteRTFHeader( ms : TMemoryStream );
  75.     procedure HandleCR(c : char; msout : TMemoryStream);
  76.     procedure HandleSpecialChars( c : char; msout : TMemoryStream );
  77.     procedure processStream( msin, msout : TMemoryStream );
  78.     procedure ReadStream;
  79.     procedure ReadChar( var c : char; msin : TMemoryStream; var status : ReadStatus );
  80.     procedure ReadToken( var token : chararray; msin : TMemoryStream; var c : char; var status : ReadStatus );
  81.     procedure WriteToken( var token : chararray; msout : TMemoryStream; var status : ReadStatus );
  82.     procedure ReadWriteToken( msin, msout : TMemoryStream; var c : char; var status : ReadStatus );
  83.     procedure ReadWriteComment( msin, msout : TMemoryStream; var c : char; var status : ReadStatus );
  84.     procedure ReadWriteStarComment( msin, msout : TMemoryStream; var c : char; var status : ReadStatus );
  85.     procedure ReadWriteLineComment( msin, msout : TMemoryStream; var c : char; var status : ReadStatus );
  86.     procedure ReadWriteString( msin, msout : TMemoryStream; var c : char; var status : ReadStatus );
  87.     procedure WriteChar( c : char; msout : TMemoryStream );
  88.     procedure ReadWriteChar( var c : char; msin, msout : TMemoryStream; var status : ReadStatus );
  89.   end;
  90.  
  91. var
  92.   Form1: TForm1;
  93.  
  94. implementation
  95.  
  96. {$R *.DFM}
  97.  
  98. const
  99.        INFILE = 'Strin.pas';
  100.        CR = #13;
  101.        LF = #10;
  102.        CRLF = #13#10;
  103.        TERMINATOR : char = '}';
  104.        SLASH : char = '\';
  105.        PARCODE = '\par ';
  106.        KWCODE = '\fs16\b ';  // bold for keywords
  107.        COMMENTCODE = '\fs16\cf2\b ';  // bold magenta for comments
  108.        STRINGCODE = '\fs16\cf1\b ';  // blue bold for strings
  109.        PLAINCODE = '\plain\fs16\cf0 ';  // plain black for other text
  110.        SPECIALRTFCHARS = ['{','}','\'] ;
  111.        ALPHANUMERIC =['a'..'z', 'A'..'Z', '_', '0'..'9'];
  112.        RTFHEADER = '{\rtf1\ansi\ansicpg1252\deff0\deftab720{\fonttbl{\f0\fswiss MS Sans Serif;}{\f1\froman\fcharset2 Symbol;}'+CRLF+'{\f2\fswiss MS Sans Serif;}{\f3\fswiss\fprq2 System;}}'+CRLF;
  113.        RTFCTABLE = '{\colortbl\red0\green0\blue0;\red0\green0\blue255;\red255\green0\blue255;\red0\green128\blue0;}'+CRLF+'\deflang2057\pard\plain\f0\fs16\cf0 ';
  114.  
  115.   NUMKEYWORDS = 50;
  116.   Keywords : array[0..NUMKEYWORDS] of string =
  117.               ( 'and', 'array', 'as', 'begin', 'case', 'class', 'const',
  118.               'constructor', 'destructor', 'div', 'do', 'downto', 'else',
  119.               'end', 'except', 'file', 'finally', 'for', 'function', 'if',
  120.               'implementation', 'in', 'inherited', 'interface', 'is', 'mod',
  121.               'nil', 'not', 'object', 'of', 'on', 'or', 'private', 'procedure',
  122.               'program', 'property', 'public', 'published', 'repeat', 'set',
  123.               'string', 'then', 'to', 'try', 'type', 'unit', 'until', 'uses',
  124.               'var', 'while', 'with');
  125.  
  126. function TForm1.KeyWord( token : string ) : boolean;
  127. // if token is a keyword, return true
  128. var
  129.    isKW : boolean;
  130.    i    : integer;
  131. begin
  132.    isKW := false;
  133.    i := 0;
  134.    while ((i <= NUMKEYWORDS) and (isKW = false )) do
  135.    begin
  136.    //!! Test is case-insensitive (OK for Pascal. Change this for C or Java)
  137.       if lowercase(token) = KeyWords[i] then
  138.          isKW := true
  139.       else
  140.          Inc(i);
  141.    end;
  142.    result := isKW;
  143. end;
  144.  
  145.  
  146. procedure TForm1.WriteRTFHeader( ms : TMemoryStream );
  147. begin
  148.     try
  149.       ms.WriteBuffer( RTFHEADER, strlen(RTFHEADER) );
  150.       ms.WriteBuffer( RTFCTABLE, strlen(RTFCTABLE) );
  151.    except
  152.       on EWriteError do
  153.          ShowMessage( 'Failed to write header and ctable to stream!' );
  154.    end;
  155. end;
  156.  
  157. procedure TForm1.HandleCR(c : char; msout : TMemoryStream);
  158. // deal with CR - write rtf '\para' code to stream
  159. begin
  160.    if c = CR then
  161.       msout.WriteBuffer( PARCODE, strlen( PARCODE ) );
  162. end;
  163.  
  164. procedure TForm1.HandleSpecialChars( c : char; msout : TMemoryStream );
  165. // deal with special RTF chars - add '\'
  166. begin
  167.    if c in SPECIALRTFCHARS then
  168.        msout.WriteBuffer( SLASH, sizeof( SLASH ) )
  169.    else HandleCR( c, msout );
  170. end;
  171.  
  172. procedure TForm1.WriteChar( c : char; msout : TMemoryStream );
  173. // Write both normal chars and special rtf chars
  174. begin  // deal with slashes and CRs
  175.       HandleSpecialChars(c, msout );
  176.        // then write the char
  177.       msout.WriteBuffer( c, sizeof( c ) );
  178. end;
  179.  
  180. procedure TForm1.ReadWriteChar( var c : char; msin, msout : TMemoryStream;
  181.                                      var status : ReadStatus );
  182. // write any char that is not written as part of a token, string or comment
  183. begin
  184.    WriteChar( c, msout );
  185.    ReadChar( c, msin, status );
  186. end;
  187.  
  188. procedure TForm1.ReadChar( var c : char; msin : TMemoryStream;
  189.                            var status : ReadStatus );
  190. begin
  191.   status := NOERROR;
  192.   try
  193.     if msin.position < msin.size then
  194.        msin.ReadBuffer( c, sizeof(c) );    // read one char at a time
  195.   except
  196.     on EReadError do
  197.        status := READERROR;
  198.   end;
  199. end;
  200.  
  201. procedure TForm1.ReadToken( var token : chararray; msin : TMemoryStream;
  202.           var c : char; var status : ReadStatus );
  203. // try to read an alphanumeric token
  204. var
  205.    i : integer;
  206. begin
  207.   status := NOERROR;
  208.   i := 0;
  209.   token := '';
  210.   try
  211.     while (c in ALPHANUMERIC) do
  212.     begin
  213.        token[i] := c;
  214.        INC(i);
  215.        ReadChar( c, msin, status );     // read one char at a time
  216.     end;
  217.     if token <> '' then
  218.        status := SUCCESS;
  219.   except
  220.     on EReadError do
  221.        status := READERROR;
  222.   end;
  223. end;
  224.  
  225. procedure TForm1.WriteToken( var token : chararray; msout : TMemoryStream; var status : ReadStatus );
  226. begin
  227.    status := NOERROR;
  228.    if KeyWord(token) then
  229.    begin
  230.       msout.WriteBuffer( KWCODE, length( KWCODE ) );
  231.       msout.WriteBuffer( token, length( token ) );
  232.       msout.WriteBuffer( PLAINCODE, length( PLAINCODE ) );
  233.    end
  234.    else
  235.       msout.WriteBuffer( token, length( token ) );
  236. end;
  237.  
  238.  
  239. procedure TForm1.ReadWriteToken( msin, msout : TMemoryStream;
  240.           var c : char; var status : ReadStatus );
  241. var
  242.    token : chararray;
  243. begin
  244.    ReadToken(token,msin,c,status);
  245.    if status = SUCCESS then
  246.       WriteToken(token,msout,status);
  247. end;
  248.  
  249. procedure TForm1.ReadWriteComment( msin, msout : TMemoryStream;
  250.           var c : char; var status : ReadStatus );
  251. // try to read and write a standard curly-brace { } comment
  252. begin
  253.   status := NOERROR;
  254. //   try           // !! add error handling here
  255.    if c = '{' then
  256.    begin
  257.      msout.WriteBuffer( COMMENTCODE, strlen( COMMENTCODE ) );
  258.      msout.WriteBuffer( SLASH, sizeof( SLASH ) );
  259.      msout.WriteBuffer( c, sizeof( c ) );
  260.      while (c <> '}') and (msin.position < msin.size) do
  261.      begin
  262.         msin.ReadBuffer( c, sizeof( c ) );
  263.         if c <> '}' then
  264.           WriteChar( c, msout );
  265.      end;
  266.      if msin.position < msin.size then
  267.      begin
  268.         msout.WriteBuffer( SLASH, sizeof( SLASH ) );
  269.         msout.WriteBuffer( c, sizeof( c ) );
  270.         msout.WriteBuffer(PLAINCODE, strlen(PLAINCODE));
  271.         msin.ReadBuffer( c, sizeof( c ) );
  272.         status := SUCCESS;
  273.      end;
  274.    end; // if c = '{' then
  275. end;
  276.  
  277. procedure TForm1.ReadWriteStarComment( msin, msout : TMemoryStream;
  278.           var c : char; var status : ReadStatus );
  279. // try to read and write a brace-star (* *) comment
  280. var
  281.    c2 : char;
  282.    CommentEnd, StarFound : boolean;
  283. begin
  284.   status := NOERROR;
  285.   c2 := '#';
  286.   CommentEnd := false;
  287.   StarFound := false;
  288. //   try           // !! add error handling
  289.    if c = '(' then
  290.    begin
  291.       ReadChar(c2,msin,status);
  292.       if c2 <> '*' then
  293.       begin        // if not a comment then
  294.           WriteChar( c, msout );
  295.           c := c2; // return c2
  296.       end
  297.       else
  298.       begin       // if this is a comment
  299.         msout.WriteBuffer( COMMENTCODE, strlen( COMMENTCODE ) );
  300.         msout.WriteBuffer( c, sizeof( c ) );
  301.         msout.WriteBuffer( c2, sizeof( c2 ) );
  302.         while not (CommentEnd) and (msin.position < msin.size) do
  303.         begin
  304.            ReadChar( c, msin, status );
  305.            if (c = ')') and StarFound then
  306.               CommentEnd := true
  307.            else
  308.            if c = '*' then
  309.               StarFound := true
  310.            else StarFound := false;
  311.            WriteChar( c, msout );
  312.         end;
  313.         if msin.position < msin.size then
  314.         begin
  315.            msout.WriteBuffer(PLAINCODE, strlen(PLAINCODE));
  316.            msin.ReadBuffer( c, sizeof( c ) );
  317.       //     status := SUCCESS;
  318.         end;
  319.       end;
  320.       status := SUCCESS;
  321.    end;
  322. end;
  323.  
  324. procedure TForm1.ReadWriteLineComment( msin, msout : TMemoryStream;
  325.           var c : char; var status : ReadStatus );
  326. // try to read and write a single-line // comment
  327. var
  328.    c2 : char;
  329. begin
  330.   status := NOERROR;
  331.   c2 := '#';
  332. //   try           // !! add error handling
  333.    if c = '/' then
  334.    begin
  335.       ReadChar(c2,msin,status);
  336.       if c2 <> '/' then
  337.       begin        // if not a comment then
  338.           WriteChar( c, msout );
  339.           c := c2; // return c2
  340.       end
  341.       else
  342.       begin       // if this is a comment
  343.         msout.WriteBuffer( COMMENTCODE, strlen( COMMENTCODE ) );
  344.         msout.WriteBuffer( c, sizeof( c ) );
  345.         msout.WriteBuffer( c2, sizeof( c2 ) );
  346.         while not (c = CR) and (msin.position < msin.size) do
  347.         begin
  348.            ReadChar( c, msin, status );
  349.            WriteChar( c, msout );
  350.         end;
  351.         if msin.position < msin.size then
  352.         begin
  353.            msout.WriteBuffer(PLAINCODE, strlen(PLAINCODE));
  354.            msin.ReadBuffer( c, sizeof( c ) );
  355.         end;
  356.       end;
  357.       status := SUCCESS;
  358.    end;
  359. end;
  360.  
  361.  
  362. procedure TForm1.ReadWriteString( msin, msout : TMemoryStream;
  363.           var c : char; var status : ReadStatus );
  364. begin
  365.   status := NOERROR;
  366. //   try           // !! add error handling
  367.    if c = '''' then
  368.    begin
  369.      msout.WriteBuffer( STRINGCODE, strlen( STRINGCODE ) );
  370.      msout.WriteBuffer( c, sizeof( c ) );
  371.      c := '#';
  372.      while (c <> '''') and (msin.position < msin.size) do
  373.      begin
  374.         msin.ReadBuffer( c, sizeof( c ) );
  375.         WriteChar( c, msout );
  376.      end;
  377.      if msin.position < msin.size then
  378.      begin
  379.         msout.WriteBuffer(PLAINCODE, strlen(PLAINCODE));
  380.         msin.ReadBuffer( c, sizeof( c ) );
  381.         status := SUCCESS;
  382.      end;
  383.    end;
  384. end;
  385.  
  386. procedure TForm1.processStream( msin, msout : TMemoryStream );
  387. var
  388.    c : char;
  389.    status : ReadStatus;
  390. begin
  391.    Screen.Cursor := crHourglass;       // Show hourglass cursor
  392.    c := '#';
  393.    // start by reading 1st char
  394.    if msin.position < msin.size then
  395.       ReadChar(c,msin,status);
  396.    try
  397.       while msin.position < msin.size do     // iterate through input stream
  398.       begin
  399.          ReadWriteToken( msin,msout,c,status);
  400.          if status <> SUCCESS then
  401.            ReadWriteString( msin,msout,c,status);
  402.          if status <> SUCCESS then
  403.            ReadWriteComment( msin,msout,c,status);
  404.          if status <> SUCCESS then
  405.            ReadWriteStarComment( msin,msout,c,status);
  406.          if status <> SUCCESS then
  407.            ReadWriteLineComment( msin,msout,c,status);
  408.          if status <> SUCCESS then
  409.             ReadWriteChar(c, msin, msout, status );
  410.       end; // while msin.position < msin.size
  411.    finally
  412.      Screen.Cursor := crDefault;    // restore normal cursor
  413.    end;
  414. end;
  415.  
  416.  
  417.  
  418. procedure TForm1.ReadStream;
  419. var
  420.    msin, msout : TMemoryStream;
  421. begin
  422.    msin := TMemoryStream.Create;
  423.    msout := TMemoryStream.Create;
  424.    msin.LoadFromFile(INFILE);
  425.    msin.Position := 0;  // set pos to 0 prior to loading from stream
  426.    msout.Position := 0;
  427.    WriteRTFHeader( msout );
  428.    processStream(msin, msout);
  429.    msout.WriteBuffer(TERMINATOR, sizeof(TERMINATOR)); // terminate RTF buffer
  430.    msout.Position := 0;
  431.    RichEdit1.Lines.LoadFromStream(msout);
  432.    msin.Free;
  433.    msout.Free;
  434. end;
  435.  
  436. procedure TForm1.LoadFileBtnClick(Sender: TObject);
  437. begin
  438.    ReadStream;
  439. end;
  440.  
  441. procedure TForm1.FormCreate(Sender: TObject);
  442. begin
  443.   RichEdit1.Plaintext := false;
  444.   PlainTextRBtn.Checked := RichEdit1.PlainText;
  445. end;
  446.  
  447. procedure TForm1.PlainTextRBtnClick(Sender: TObject);
  448. begin
  449.    RichEdit1.Plaintext := PlainTextRBtn.Checked;
  450. end;
  451.  
  452. end.
  453.