Name

Preprocess::Ops - Preprocess ◁, ◀, ▷ and ▶ as operators in ANSI-C.

Synopsis

See the final lines of: https://github.com/philiprbrenan/C/blob/master/c/z/arenaTree/arenaTree.c for working examples of the following operators.

Method dispatch operators: ▷ and ▶

Preprocess ▷ and ▶ as method dispatch by translating:

p = node ▷ key("a");

to:

p = node . proto->key(&node, "a");

and:

p = node ▶ key("a");

to:

p = node -> proto->key(node, "a");

Constant and variable creation operators: ◁ and ◀

Preprocess instances of ◁ as a constant creation operator:

c ◁ sfc("cba");

to get:

const typeof(sfc("cba")) c = sfc("cba");

Preprocess instances of ◀ as a variable creation operator:

d ◀ sfc("cba");

to get:

typeof(sfc("cba")) c = sfc("cba");

which, in effect, produces:

const char c = sfc("cba");
      char d = sfc("cba");

in the context of:

char sfc(char *s) {return *s;}

int main(void) {
  c ◁ sfc("cba");
  d ◀ sfc("cba");
  assert(c == 'c');
}

Here documents via ◉

Preprocess instances of ◉ as starting a here document:

char *c = ◉;
a
b
◉

to get:

char *a =
"a\n"
"  b\n"
;

Using ✓ or ✔ for assert(...);

Convert instances of or as in:

✔ a == 1;

to:

assert(a == 1);

to make assert function calls more prominent in tests.

Conversely as in:

  a ◁ 0;
✗ a;

converts to:

const int a = 0;
assert(!a);

Using ≞ for memcpy(...);

Convert instances of as in:

Colour c;
c ≞ makeColour(0,1,0,1);

to:

Colour c;
memcpy(
 ((void *)&c,
 ({typeof(c) s = makeColour(0,1,0,1); (void *)&s;}),
 sizeof(c));

to allow piece-wise initialization of structures in memory with constant elements.

The special case of initialization with a constant zero:

c ≞ 0;

produces:

memset(c, 0, sizeof(c));

Using ≞≞ for !memcmp(...)

Convert instances of ≞≞ and !≞ as in:

typedef struct
 {int a;
  int b;
 } S;
S t,   s;  s.a = 1; s.b = 2;
  t  ≞ s;  // A
✓ s ≞≞ t;  // B
  t  ≞ 0;  // C
✓ s !≞ t;  // D

to get:

// A
({typeof( t)  sourcesourcesource = s;
  memcpy(&t, &sourcesourcesource,
  sizeof( t));
});

// B
assert(
 ({typeof(s) targettargettarget = s,
             sourcesourcesource = t;
   !memcmp(&targettargettarget, &sourcesourcesource, sizeof(s));
  }));

// C
memset(&t, 0, sizeof(t));

// D
assert(
 ({typeof(s)targettargettarget = s, sourcesourcesource = t;
   memcmp( &targettargettarget,    &sourcesourcesource,
   sizeof(s));
 }));

to allow compact comparisons of structures in memory.

Using ≈≈ for !strcmp(...);

Convert instances of ≈≈ and !≈ as in:

{  a ≋ 12; b ≋ 12; c ≋ 12;
   a ≈ "aaaa";
   b ≈ "bbbb";
   A ◁ c +≈ a; B ◁ A +≈ b;
   C ◁ c ∼  b;

 ✓ a ≈≈ "aaaa";  // AAAA
 ✓ b ≈≈ "bbbb";
 ✓ b !≈ a;       // BBBB
 ✓ c ≈≈ "aaaabbbb";
 ✓ A ≈≈ b;
 ✓ B ≈≈ "";
 ✓ C ≈≈ b;

to get:

assert(!strcmp(a, "aaaa"); // AAAA

assert( strcmp(b, "bbbb"); // BBBB

to create readable string equality operations.

Using ≋ to declare strings.

Strings can be declared using the operator to supply a length, so that:

N ◁ 12;
a ≋ N+1;

becomes:

char a[13];

via:

const int N = 12;
char    a[N + 1];

Using +≈ to concatenate strings.

Strings can be concatenated with the +≈ operator, so that:

c ≋ 12;
a ◁ c +≈ "aaaa";
b ◁ a +≈ "aaaa";
✓ c ≈≈ "aaaabbbb";

becomes:

char c[12];
char *a = stpcpy(c, "aaaa");
char *b = stpcpy(a, "bbbb");
assert(!strcmp(c,   "aaaabbbb");

Using ∼ to search strings.

Strings can be searched using the operator to supply a search string as in:

C ◁ c ∼ b;

which becomes:

char *C = strstr(a, b);

Replacing $ with the base file name.

Occurrences of the $ character are replaced by the base name of the file containing the source with the first letter capitalized, so that:

typedef struct $Node {...} $Node;

in a file called tree.c becomes:

typedef struct TreeNode {...} TreeNode;

new operator

Occurrences of:

new XXX

are replaced by:

newXXX(({struct XXX t = {proto: &ProtoTypes_XXX}; t;}))

Occurrences of:

new XXX(a:1)

are replaced by:

newXXX(({struct XXX t = {a:1, proto: &ProtoTypes_XXX}; t;}))

The prototype vectors are generated by examining all the methods defined in the c file. The prototype vectors are written to the specified h file which should be included in the c file for use via the ▷ and ▶ operators.

Marking tests with //T

//T immediately followed by the name of a method up to its first _ (if any or the end of the name otherwise) marks a function as testing all the methods that start with that name:

void test10()                        //Tsystem //TprintsAs
 {  a ◁ make$FromString("uname");
    a ▷ system;
  ✓ a ▷ containsString("Linux");
  ✓ a ▷ printsAs(◉);
Linux
◉
    a ▷ free;
 }

Function test10 is marked as testing both system_string and printsAs_stringBuffer_string. Functions that are declared static but have no associated tests are listed in the preprocessor output as in:

The following methods need tests:
  parseXmlFromString

after preprocessing a file called xml.c containing:

static $Parse parse$FromString_$Parse_StringBuffer
 (StringBuffer string)
 {return make$ParseFromString(0, string, strlen(string));
 }

with no test function marked with //TparseXmlFromString.

Preprocessor commands

duplicate

The duplicate command generates the previous function with the changes indicated in the words following the command as in:

static char * key_$Node                                                       // Get the key for a node
 (const $Node n)                                                              // Node
 {return n.key;
 }
duplicate s/key/data/g

which adds the following code to the current output file:

static char * data_$Node                                                      // Get the data for a node
 (const $Node n)                                                              // Node
 {return n.data;
 }

exports

The exports command provides a name for or a collection of functions that can be included in generated output files, for instance:

exports aaa new$Node key_$Node

creates a new set of exports called aaa which contains the two functions mentioned. As these names have $ in them they will be expanded with the base name of the file into which they are being copied.

include

The include command copies the named function, structures, and exported collections from the specified file into the current output file. For instance:

include ../arenaTree.c :arena !key_$Node data_$Node

reads the relative file ../arenaTree.c and copies in all the structures mentioned in collection arena except for key_$node as well as copying the explicitly mentioned function data_$Node.

Public Version Control on GitHub

Please submit changes as pull requests on https://github.com/philiprbrenan/PreprocessOps

Description

Preprocess ◁, ◀, ▷ and ▶ as operators in ANSI-C.

Version 20201117.

The following sections describe the methods in each functional area of this module. For an alphabetic listing of all methods by name see Index.

Preprocess

Preprocess ◁, ◀, ▷ and ▶ as operators in ANSI-C.

c($inputFile, $cFile, $hFile, $column)

Preprocess ▷ and ▶ as method dispatch operators in ANSI-C.

   Parameter   Description
1  $inputFile  Input file
2  $cFile      C output file
3  $hFile      H output file
4  $column     Optional start column for comments (80)

Example:

if (88) {
  my $d  = q(zzz);
  my $ds = fpd($d,  qw(source));
  my $dd = fpd($d,  qw(derived));

  my $sc = fpe($ds, qw(node c));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $dc = fpe($dd, qw(node c));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  my $dh = fpe($dd, qw(node h));

  owf($sc, <<END);
#include <stdio.h>

typedef struct Node                                                             // Node
 {const struct ProtoTypes_Node *proto;
  int data;
 } Node;

#include "node.h"

static Node by                                                                  // New from node * number
 (const Node * n,                                                               // Node
  const int    i)                                                               // Multiplier
 {return new Node(data: i * n->data);
 }

static void dump                                                                // Dump a node to stdout
 (const Node * n)                                                               // Node to dump
 {printf("data=%d\
", n->data);
 }

int main(void)                                                                  //TnewNode //Tdump //Tby
 {a ◁ new Node(data: 6);
  b ◁ a ▷ by(7);
  b ▷ dump;
  return 0;
 }
END


  my $r = c($sc, $dc, $dh);                                                     # Preprocess source c to get derived c  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $c = qq((cd $dd; gcc node.c -o a; ./a));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply scalar(qx($c)), "data=42
";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



  is_deeply readCFile($dc), <<'END';                                            # Generated base.c  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


#line 1 "node.c"  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

#include <stdio.h>

typedef struct Node                                                             // Node
 {const struct ProtoTypes_Node *proto;
  int data;
 } Node;

#include "node.h"

static Node by                                                                  // New from node * number
 (const Node * n,                                                               // Node
  const int    i)                                                               // Multiplier
 {return newNode(({struct Node t = {data: i * n->data, proto: &ProtoTypes_Node}; t;}));
 }

static void dump                                                                // Dump a node to stdout
 (const Node * n)                                                               // Node to dump
 {printf("data=%d
", n->data);
 }

int main(void)                                                                  //TnewNode //Tdump //Tby
 {const typeof(newNode(({struct Node t = {data: 6, proto: &ProtoTypes_Node}; t;}))) a = newNode(({struct Node t = {data: 6, proto: &ProtoTypes_Node}; t;}));
  const typeof(a.proto->by(&a, 7)) b = a.proto->by(&a, 7);
  b.proto->dump(&b);
  return 0;
 }
END

  is_deeply readCFile($dh), <<END;                                              # Generated include file
static Node by
 (const Node * n,
  const int    i);
static void dump
 (const Node * n);
int main(void);
struct ProtoTypes_Node {
  Node  (*by)(                                                                  // New from node * number
    const Node * n,                                                             // Node
    const int i);                                                               // Multiplier
  void  (*dump)(                                                                // Dump a node to stdout
    const Node * n);                                                            // Node to dump
 } const ProtoTypes_Node =
{by, dump};
Node newNode(Node allocator) {return allocator;}
END

  clearFolder($d, 10);
 }

if (36) {
  my $d = q(zzz);

  my $c = owf(fpe($d, qw(source c)), <<'END');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

#include <assert.h>
#include <stdio.h>
int main(void)
 {a ◁ ◉;
a
  b
◉
  ✓ a[0] == 'a';
  printf("%s", a);
 }
END

  my $h = fpe($d, qw(source  h));

  my $g = fpe($d, qw(derived c));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



  my $r = c($c, $g, $h);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



  is_deeply scalar(qx(cd $d; gcc derived.c -o a; ./a)), <<END, 'aaaa';  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

a
  b
END

  is_deeply readCFile($g), <<'END', 'bbbb';

#line 1 "source.c"  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

#include <assert.h>
#include <stdio.h>
int main(void)
 {const typeof("a
" "  b
") a = "a
" "  b
";
//◉a
//◉  b
//◉
  assert( a[0] == 'a');
  printf("%s", a);
 }
END
  clearFolder($d, 10);
 }

if (26) {
  my $d = q(zzz);

  my $c = owf(fpe($d, qw(source c)), <<'END');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
 {typedef struct
   {int a;
    int b;
   } S;
  S s; s.a = 1; s.b = 2;
  S t;
    t ≞ s;  ✓ s ≞≞ t;
    t ≞ 0;  ✓ s !≞ t;

  S *ps = &s, *pt = &t;

   t ≞ 0; ✓ t.a == 0;   ✓ t.b == 0;    ✓ pt !◧ s;
  pt ◧ s; ✓ t.a == s.a; ✓ t.b == s.b;  ✓ pt ◧◧ s;

   s ≞ 0; ✓ s.a == 0;   ✓ s.b == 0;    ✓ t !◨ ps;
  ps ◧ t; ✓ s.a == t.a; ✓ s.b == t.b;  ✓ t ◨◨ ps;
  printf(◉);
success
◉
 }

if (26) {
  my $d = q(zzz);

  my $c = owf(fpe($d, qw(source c)), <<'END');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)

 {  a ≋ 12; b ≋ 12; c ≋ 12;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    a ≈ "aaaa";
    b ≈ "bbbb";

    A ◁ c +≈ a; B ◁ A +≈ b;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    C ◁  c ∼ b;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  ✓ a ≈≈ "aaaa";
  ✓ b ≈≈ "bbbb";
  ✓ b !≈ a;

  ✓ c ≈≈ "aaaabbbb";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  ✓ A ≈≈ b;
  ✓ B ≈≈ "";
  ✓ C ≈≈ b;

  printf(◉);
success
◉
 }
END

  my $h = fpe($d, qw(source  h));

  my $g = fpe($d, qw(derived c));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $r = c($c, $g, $h);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply scalar(qx(cd $d; gcc -g -Wall derived.c -o a; ./a)), <<END;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

success
END
# clearFolder($d, 10);
 }

PreprocessOpsMap Definition

Methods and structures in the C file being preprocessed

Output fields

methods

Methods.

structures

Structure definitions.

PreprocessOpsParse Definition

Structure of the C program being preprocessed

Output fields

methods

Methods.

structureParameters

Structures used as parameters

structures

Structure definitions.

testsFound

Tests found

testsNeeded

Tests still needed

PreprocessOpsStruct Definition

Structure declaration

Output fields

comment

Comment for structure

flags

Flags for structure

methods

Methods.

name

Name of structure

structureParameters

Structures used as parameters

structures

Structure definitions.

testsFound

Tests found

testsNeeded

Tests still needed

Private Methods

trimComment($s)

Remove trailing white space and comment

   Parameter  Description
1  $s         String

method($line)

Check whether a line of C code defines a method, returning (return, name, flags, comment) if it is, else ()

   Parameter  Description
1  $line      Line of C code

structure($line)

Check whether a line of C code defines a structure, returning (name, flags, comment) if it is, else ()

   Parameter  Description
1  $line      Line of C code

mapCode($file)

Find the structures and methods defined in a file

   Parameter  Description
1  $file      Input file

printData($lineNumber, $line)

Print statement

   Parameter    Description
1  $lineNumber  Code line number
2  $line        Code line

duplicateFunction($lineNumber, $inputFile, $code)

Duplicate the previous function with the specified changes applied

   Parameter    Description
1  $lineNumber  Line number of line being expanded
2  $inputFile   File containing line being expanded
3  $code        Lines of code

includeFile($lineNumber, $inputFile, $cFile, $hFile, $code)

Expand include files so that we can pull in code and structures from other files in the includes folder.

   Parameter    Description
1  $lineNumber  Line number of line being expanded
2  $inputFile   File containing line being expanded
3  $cFile       Output C file
4  $hFile       Output H file
5  $code        Line of code

Index

1 c - Preprocess ▷ and ▶ as method dispatch operators in ANSI-C.

2 duplicateFunction - Duplicate the previous function with the specified changes applied

3 includeFile - Expand include files so that we can pull in code and structures from other files in the includes folder.

4 mapCode - Find the structures and methods defined in a file

5 method - Check whether a line of C code defines a method, returning (return, name, flags, comment) if it is, else ()

6 printData - Print statement

7 structure - Check whether a line of C code defines a structure, returning (name, flags, comment) if it is, else ()

8 trimComment - Remove trailing white space and comment

Installation

This module is written in 100% Pure Perl and, thus, it is easy to read, comprehend, use, modify and install via cpan:

sudo cpan install Preprocess::Ops

Author

philiprbrenan@gmail.com

http://www.appaapps.com

Copyright

Copyright (c) 2016-2019 Philip R Brenan.

This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.