ffi/preprocessor

Simple C Preprocessor


Keywords
parser, headers, preprocessor, compiler, c, ffi, php
License
MIT

Documentation

Simple C-lang Preprocessor

This implementation of a preprocessor based in part on ISO/IEC 9899:TC2.

Requirements

  • PHP >= 7.4

Installation

Library is available as composer repository and can be installed using the following command in a root of your project.

$ composer require ffi/preprocessor

Usage

use FFI\Preprocessor\Preprocessor;

$pre = new Preprocessor();

echo $pre->process('
    #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;

    #if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE)
        #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
            #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
        #else
            #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
        #endif
    #endif

    VK_DEFINE_HANDLE(VkInstance)
    VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore)
');

//
// Expected Output:
//
//  typedef struct VkInstance_T* VkInstance;
//  typedef uint64_t VkSemaphore;
//

Directives

Supported Directives

  • #include "file.h" local-first include
  • #include <file.h> global-first include
  • #define name defining directives
    • #define name value object-like macro
    • #define name(arg) value function-like macro
    • #define name(arg) xxx##arg concatenation
    • #define name(arg) #arg stringizing
  • #undef name removing directives
  • #ifdef name "if directive defined" condition
  • #ifndef name "if directive not defined" condition
  • #if EXPRESSION if condition
  • #elif EXPRESSION else if condition
  • #else else condition
  • #endif completion of a condition
  • #error message error message directive
  • #warning message warning message directive
  • #line 66 "filename" line and file override
  • #pragma XXX compiler control
    • #pragma once
  • #assert XXX compiler assertion
    • #unassert XXX compiler assertion
  • #ident XXX
    • #sccs XXX

Expression Grammar

Comparison Operators

  • A > B greater than
  • A < B less than
  • A == B equal
  • A != B not equal
  • A >= B greater than or equal
  • A <= B less than or equal

Logical Operators

  • ! A logical NOT
  • A && B conjunction
  • A || B disjunction
  • (...) grouping

Arithmetic Operators

  • A + B math addition
  • A - B math subtraction
  • A * B math multiplication
  • A / B math division
  • A % B modulo
  • A++ increment
    • ++A prefix form
  • A-- decrement
    • --A prefix form
  • +A unary plus
  • -A unary minus
  • &A unary addr
  • *A unary pointer

Bitwise Operators

  • ~A bitwise NOT
  • A & B bitwise AND
  • A | B bitwise OR
  • A ^ B bitwise XOR
  • A << B bitwise left shift
  • A >> B bitwise right shift

Other Operators

  • defined(X) defined macro
  • A ? B : C ternary
  • sizeof VALUE sizeof
    • sizeof(TYPE) sizeof type

Literals

  • true, false boolean
  • 42 decimal integer literal
    • 42u, 42U unsigned int
    • 42l, 42L long int
    • 42ul, 42UL unsigned long int
    • 42ll, 42LL long long int
    • 42ull, 42ULL unsigned long long int
  • 042 octal integer literal
  • 0x42 hexadecimal integer literal
  • 0b42 binary integer literal
  • "string" string (char array)
    • L"string" string (wide char array)
    • "\•" escape sequences in strings
    • "\•••" arbitrary octal value in strings
    • "\X••" arbitrary hexadecimal value in strings
  • 'x' char literal
    • '\•' escape sequences
    • '\•••' arbitrary octal value
    • '\X••' arbitrary hexadecimal value
    • L'x' wide character literal
  • 42.0 double
    • 42f, 42F float
    • 42l, 42L long double
    • 42E exponential form
    • 0.42e23 exponential form
  • NULL null macro

Type Casting

  • (char)42
  • (short)42
  • (int)42
  • (long)42
  • (float)42
  • (double)42
  • (bool)42 (Out of ISO/IEC 9899:TC2 specification)
  • (string)42 (Out of ISO/IEC 9899:TC2 specification)
  • (void)42
  • (long type)42 Casting to a long type (long int, long double, etc)
  • (const type)42 Casting to a constant type (const char, etc)
  • (unsigned type)42 Casting to unsigned type (unsigned int, unsigned long, etc)
  • (signed type)42 Casting to signed type (signed int, signed long, etc)
  • Pointers (void *, etc)
  • References (unsigned int, unsigned long, etc)

Object Like Directive

use FFI\Preprocessor\Preprocessor;
use FFI\Preprocessor\Directive\ObjectLikeDirective;

$pre = new Preprocessor();

// #define A
$pre->define('A');

// #define B 42
$pre->define('B', '42');

// #define С 42
$pre->define('С', new ObjectLikeDirective('42'));

Function Like Directive

use FFI\Preprocessor\Preprocessor;
use FFI\Preprocessor\Directive\FunctionLikeDirective;

$pre = new Preprocessor();

// #define C(object) object##_T* object;
$pre->define('C', function (string $arg) {
    return "${arg}_T* ${arg};";
});

// #define D(object) object##_T* object;
$pre->define('D', new FunctionLikeDirective(['object'], 'object##_T* object'));

Include Directories

use FFI\Preprocessor\Preprocessor;

$pre = new Preprocessor();

$pre->include('/path/to/directory');
$pre->exclude('some');

Message Handling

use FFI\Preprocessor\Preprocessor;

$logger = new Psr3LoggerImplementation();

$pre = new Preprocessor($logger);

$pre->process('
    #error Error message
    // Will be sent to the logger:
    //  - LoggerInterface::error("Error message")
    
    #warning Warning message
    // Will be sent to the logger: 
    //  - LoggerInterface::warning("Warning message")
');