Jump to page: 1 2 3
Thread overview
Macro text preprocessor
Aug 23, 2005
Walter
Aug 23, 2005
Walter
Aug 23, 2005
ElfQT
Aug 23, 2005
Walter
Aug 23, 2005
Hasan Aljudy
Aug 23, 2005
Walter
Aug 23, 2005
AJG
Aug 24, 2005
Derek Parnell
Aug 24, 2005
Don Clugston
Aug 25, 2005
ElfQT
Aug 25, 2005
Walter
Aug 25, 2005
Chris Sauls
Aug 25, 2005
AJG
Aug 25, 2005
Derek Parnell
Aug 25, 2005
ElfQT
Aug 26, 2005
Walter
Aug 24, 2005
Stewart Gordon
Aug 24, 2005
Uwe Salomon
Aug 24, 2005
Stewart Gordon
Aug 24, 2005
Stewart Gordon
Aug 24, 2005
Walter
August 23, 2005
With all this talk about generating HTML files with boilerplate, I use a simple macro text preprocessor to do it. Here it is, and btw it shows what can be done with just a few lines of D!
---------------------------------
// Copyright (C) 2005 by Digital Mars www.digitalmars.com
// All Rights Reserved
// Written by Walter Bright

import std.file;
import std.stdio;
import std.c.stdlib;
import std.string;
import std.ctype;
import std.date;

void usage()
{
    printf(
"Macro process file.\n"
"Use:\n"
"    macro infile outfile args...\n"
"    args are of the form name=value\n"
"    $(name) in text is replaced with value\n"
 );
}

int main(char[][] cmdargs)
{
    int i;
    char[] arg;
    char[][] args;
    char[] fin = null;
    char[] fout = null;
    int errors;

    writefln("macro 1.01");

    if (cmdargs.length < 3)
    { usage();
 return EXIT_FAILURE;
    }

    errors = 0;
    args.length = cmdargs.length - 3;
    args.length = 0;
    for (i = 1; i < cmdargs.length; i++)
    { arg = cmdargs[i];
 if (i <= 2)
 {
     if (arg[0] == '-')
     {
  writefln("unrecognized switch '%s'", arg);
  errors++;
     }
     else if (i == 1)
  fin = arg;
     else
  fout = arg;
 }
 else
     args ~= arg;
    }
    if (errors)
 return EXIT_FAILURE;

    predefinedMacros();

    foreach (char[] arg; args)
 macroInsert(arg);

    char[] input = cast(char[])std.file.read(fin);
    char[] output;

    output = process(input);

    std.file.write(fout, output);

    return EXIT_SUCCESS;
}

char[] [char[]] mactab;

void macroInsert(char[] arg)
{
    int i = find(arg, '=');
    if (i == -1)
 throw new Error("expected name=value, not " ~ arg);
    char[] name = strip(arg[0 .. i]);
    char[] value = stripl(arg[i + 1 .. length]);
    mactab[name] = value;
}

void predefinedMacros()
{   d_time t = getUTCtime();

    char[] value = toDateString(t);
    mactab["__DATE__"] = value;

    value = format(cast(long)YearFromTime(t));
    mactab["__YEAR__"] = value;
}

char[] process(char[] input)
{
    char[][] lines;
    char[] output;

    lines = splitlines(input);

    // Do backsplash line splicing
    for (int i = 0; i + 1 < lines.length; )
    {
 char[] line = lines[i];

 if (line.length && line[length - 1] == '\\')
 {
     line.length = line.length - 1; // chop off backslash
     line ~= '\n';
     line ~= lines[i + 1];
     lines[i] = line;
     for (int j = i + 1; j + 1 < lines.length; j++)
  lines[j] = lines[j + 1];
     lines.length = lines.length - 1;
     //writefln("line = '%s'", line);
 }
 else
     i++;
    }

    // Macro process lines
    char[] result;
    foreach (char[] line; lines)
    {
 result.length = 0;

 for (int i = 0; i < line.length; )
 {   int j;

     j = find(line[i .. length], '$');
     if (j == -1)
     {
  result ~= line[i .. length];
  break;
     }
     j += i;
     result ~= line[i .. j];
     if (j + 1 == line.length)
     {
  result ~= '$';
  break;
     }
     if (line[j + 1] == '$')
     {
  result ~= '$';
  i = j + 2;
  continue;
     }
     if (isalpha(line[j + 1]))
     {
  result ~= mactab[ line[j + 1 .. j + 2] ];
  i = j + 2;
  continue;
     }
     if (line[j + 1] == '(' && j + 2 != line.length)
     {
  int k = find(line[j + 2 .. length], ')');
  if (k != -1)
  {
      k += j + 2;
      result ~= mactab[ line[j + 2 .. k] ];
      i = k + 1;
      continue;
  }
     }
     result ~= '$';
     i = j + 1;
 }

 if (result.length && result[0] == '.')
 {
     if (result.length >= 4 && result[1 .. 4] == "set")
     {
  macroInsert(result[4 .. length]);
  continue;
     }
     else if (result.length >= 8 && result[1 .. 8] == "include")
     { char[] filename = strip(result[8 .. length]);
  char[] input = cast(char[])std.file.read(filename);
  output ~= process(input);
  continue;
     }
     else
  throw new Error("unknown dot command: " ~ result);
 }
 else
 {
     output ~= result;
     output ~= '\n';
 }
    }

    return output;
}


August 23, 2005
Here's an example of how I use it, this is std_random.txt, the eventual html is at www.digitalmars.com/d/phobos/std_random.html

The header, footer, and sidebar are all boilerplate from macros.inc.

---------------------------------------------------------------------------
.set TITLE=Digital Mars - The D Programming Language - std.random
.set WIKI=Phobos/StdRandom
.include macros.inc

$(HEADER)
$(PHOBOS_S)

<h1 align=center>std.random</h1>


<dl><dl>

 <dt>void <b>rand_seed</b>(uint seed, uint index)
 <dd>The random number generator is seeded at program
 startup with a random value. This ensures that each program
 generates a different sequence of random numbers.
 To generate a repeatable sequence, use <b>rand_seed</b>() to
 start the sequence. <i>seed</i> and <i>index</i> start it, and each
 successive value increments <i>index</i>. This means that the
 <i>n</i>th random number of the sequence can be directly generated
 by passing <i>index + n</i> to <b>rand_seed</b>().
 <p>

 <dt>uint <b>rand</b>()
 <dd>Get next random number in sequence.
 <p>

</dl></dl>


$(PHOBOS_E)
$(FOOTER)




August 23, 2005
In "no frames" conversation I already written about XML commenting.
Let me do it again. Why not store the same information in the same place:
the code file?

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcoriXMLDocumentation.asp

Your example would look like:

/// <summary>
/// The random number generator is seeded at program startup with a random
value.
/// This ensures that each program generates a different sequence of random
numbers. /// To generate a repeatable sequence, use <see
cref="rand_seed()"/> to start the sequence.
/// The parameters start it, and each successive value increments index.
This means that the nth random number of the sequence can be directly
generated by passing index + n to <see cref="rand_seed()"/>.
/// </summary>
/// <param name="seed">the random number seed</param>
/// <param name="index">each successive value increments index</param>
/// <returns>void<returns>
void rand_seed(uint seed, uint index)
{
     seed = s;
     index = i;
}

/// <summary>
/// Get next random number in sequence.
/// </summary>
/// <returns>next random number<returns>
uint rand()
{
   ...code...
}

And simle tools should work with this information after, fully
cross/hyperlinked.
Also, you don't need to write return and parameter types, it's already in
the code.
Also, compiler should check if the doc (xml comment) is outdated.

If you have a problem with this, as it's sooo Microsoft-ish, well... Do something similar/equivalent... (But why reinvent the wheel?)

If you are disturbed with comment in code... well first, unittest also
appears to be there, and second, code editor should collapse the comment
blocks.
(Also, I'd like to ask if unittests _have_ to be in the same file than the
tested code?)

Regards,
 ElfQT


August 23, 2005
"ElfQT" <dethjunk@yahoo.com> wrote in message news:deg1tc$26sq$1@digitaldaemon.com...
> If you have a problem with this, as it's sooo Microsoft-ish, well... Do something similar/equivalent... (But why reinvent the wheel?)

While it's a good idea, I find it just aesthetically ugly in the program source code.


> (Also, I'd like to ask if unittests _have_ to be in the same file than the
> tested code?)

Nope.


August 23, 2005
Walter wrote:
> "ElfQT" <dethjunk@yahoo.com> wrote in message
> news:deg1tc$26sq$1@digitaldaemon.com...
> 
>>If you have a problem with this, as it's sooo Microsoft-ish, well...
>>Do something similar/equivalent... (But why reinvent the wheel?)
> 
> 
> While it's a good idea, I find it just aesthetically ugly in the program
> source code.
> 
> 

I agree, it's truly ugly.
How about something cleaner, like Javadoc?


August 23, 2005
"Hasan Aljudy" <hasan.aljudy@gmail.com> wrote in message news:deg8gq$2n93$1@digitaldaemon.com...
> I agree, it's truly ugly.
> How about something cleaner, like Javadoc?

Better, but still ugly.

I should probably design a proper one <g>, but I just haven't given it much thought.


August 23, 2005
Hi,

Walter wrote:
> "ElfQT" <dethjunk@yahoo.com> wrote in message
> news:deg1tc$26sq$1@digitaldaemon.com...
> 
>>If you have a problem with this, as it's sooo Microsoft-ish, well...
>>Do something similar/equivalent... (But why reinvent the wheel?)
> 
> 
> While it's a good idea, I find it just aesthetically ugly in the program
> source code.

I agree. It's ugly, _and_ verbose. XML + triple slashes + indenting is just not a good combination.

I think general-user documentation should be kept separate from the actual code. Comments in the code should be there primarily to help the people that create/maintain the module, not general programmers.

>>(Also, I'd like to ask if unittests _have_ to be in the same file than the
>>tested code?)
>
> Nope.

What about creating a default unittest file per module? A sort of code-behind variant so that all that test code can be by itself.

For instance:

Foo.d : Actual module code.
Foo.ut.d : Unittest for Foo.

I believe this would work because modules already can't contain periods. Also, if a .ut.d file doesn't exist, then it would be simply ignored.

Cheers,
--AJG.

August 24, 2005
On Tue, 23 Aug 2005 19:51:02 -0400, AJG wrote:

> Hi,
> 
> Walter wrote:
>> "ElfQT" <dethjunk@yahoo.com> wrote in message news:deg1tc$26sq$1@digitaldaemon.com...
>> 
>>>If you have a problem with this, as it's sooo Microsoft-ish, well... Do something similar/equivalent... (But why reinvent the wheel?)
>> 
>> While it's a good idea, I find it just aesthetically ugly in the program source code.
> 
> I agree. It's ugly, _and_ verbose. XML + triple slashes + indenting is just not a good combination.

~~~~ Shudder ~~~~

> I think general-user documentation should be kept separate from the actual code. Comments in the code should be there primarily to help the people that create/maintain the module, not general programmers.

I place documentation inside the source for my projects purely from a practical aspect. As I'm the one person doing both the coding and the documentation, it is more efficient for me to keep them together. Basically if they were in different files they would soon get out of synchronization. However, if I had the luxury of a separate person doing the docs, they would be separated.

That's why I've developed a documentation mark up language and a tool to extract it years ago and still use it. The Build utility's HTML docs are generated from the mark up stuff embedded in the source code.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
24/08/2005 10:03:37 AM
August 24, 2005
AJG wrote:
> Hi,
> 
> Walter wrote:
> 
>> "ElfQT" <dethjunk@yahoo.com> wrote in message
>> news:deg1tc$26sq$1@digitaldaemon.com...
>>
>>> If you have a problem with this, as it's sooo Microsoft-ish, well...
>>> Do something similar/equivalent... (But why reinvent the wheel?)
>>
>>
>>
>> While it's a good idea, I find it just aesthetically ugly in the program
>> source code.
> 
> 
> I agree. It's ugly, _and_ verbose. XML + triple slashes + indenting is just not a good combination.

I think that ideally, it would be Python-esque.
IE, whitespace in docs would be syntactically significant.
eg, a D-style comment might be
/*D
     functionName
  Author: ABC  Date:xyz  Copyright:2005

  param1   meaning of this parameter
  param2   it identifies a parameter by the whitespace at start of the line. If when finished parsing the comment, the list of parameters doesn't match the params in the comment, it's an error if #params in
comment >0. (so explaining params is optional, but if you explain one,
you must explain all, and mustn't explain any that aren't there).
 param3   this is another parameter

One-line summary

Long explanation, which
travels over several
lines.
*/
The idea being that if you begin a comment with the special D-comment
identifier, you must format it in a particular good style.
Some existing well-commented code should automatically be readable.
I don't see why you need any kind of <param> tokens,
human beings seem to be able to parse (well-formed) comments perfectly well without them.
August 24, 2005
Walter wrote:
<snip>
>  <dt>uint <b>rand</b>()
>  <dd>Get next random number in sequence.
>  <p>
> 
> </dl></dl>

Oh.  I'd begun to assume the misplaced <p> tags dotted about the pages were a bug in your preprocessor.  Looks like I was wrong.

Welcome to HTML.  The <p> tag marks the beginning of a paragraph, not the end of one.  And it certainly doesn't belong /between/ elements of a list.

There's also </p>, which marks the end of a paragraph, but that's optional....

Stewart.

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d- s:- a->--- UB@ P+ L E@ W++@ N+++ o K- w++@ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y
------END GEEK CODE BLOCK------

My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
« First   ‹ Prev
1 2 3