checkpoint before refactoring with CAT
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Thu, 27 Mar 2025 14:43:07 +0000 (14:43 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Thu, 27 Mar 2025 14:43:07 +0000 (14:43 +0000)
developer/document🖉/cpp_evaluation.org
developer/example/cpp_ext_0.c
developer/example/try_2_connectors.c

index f9deead..27f1942 100644 (file)
@@ -4,13 +4,13 @@
 
 1. Macro function definition
 
-   A macro function has three parts, 1) a name 2) a parameter list  3) definition
+   A macro function has three parts, 1) a name 2) a parameter list  3) body
 
    Parameters are declared by listing them between parenthesis after the name. No space
    can occur between the name and the parameter list! If there is space then the
-   parameter list will be taken as part of the definition.
+   parameter list will be taken as part of the body.
 
-   Text after the parameter list forms the definition.
+   Text after the parameter list forms the body.
 
       #+BEGIN_SRC c
       #define name(x ,y ,z)  <replacement>
       #define name(x ,...) <replacement>
       #+END_SRC
 
-2. Macro call
+2. Macro Call
 
-   Can occur anywhere in the source code. Takes the form of an macro function
-   name followed by an argument list.
+   A macro call can occur anywhere in the source code. It takes the form of a macro
+   name followed by an argument list:
 
-      #+BEGIN_SRC c
-      name (1 ,a ,b)
-      #+END_SRC
+   #+BEGIN_SRC c
+   name(1 ,a ,b)
+   #+END_SRC
+
+   In a call, space is allowed between the macro name and the argument list.
+
+** 1. Argument Substitution Phase
+
+   When the macro is invoked:
+
+   1. Each parameter in the macro body is replaced *verbatim* with the
+      corresponding argument. No evaluation is performed at this stage.
+
+   2. The new macro body is a new token stream: the old macro body with arguments pasted in.
+
+** 2. Evaluation Phase
+
+   The preprocessor then scans the argument substituted macro body *from left to right*,
+   evaluating macro calls *as it encounters them*.
+
+   - If a token is **not** a macro call (see below), it is included as-is.
+   - If a token **is** a macro call, it is expanded immediately, using the same two-phase process.
 
-   In a call, space is allowed between the macro name and the arguments list.
+   This produces a depth-first, left-to-right expansion strategy.
 
-    1. Each parameter in the definition is replaced with the corresponding argument.
-       
-    2. cpp then scans the definition from left to right. 
+   Non-macro-call tokens include:
 
-        2.1 Upon finding a non-macro call token cpp includes it as part of the result.
+   1. Any non-identifier (e.g. `+`, `123`, `"text"`)
 
-        2.2. Uon finding a macro call token, it does a recursive evaluation on it. 
+   2. Any identifier **not followed by `(`**
 
-   Non-macro call tokens include:
+   3. Any parameter or token used with `#`
 
-   1. a non-identifier.
+      - `#param` turns the *original* argument text into a string.
+      - Because it stringifies, *the argument is never evaluated*.
 
-   2. an identifier not followed by parenthesis.
+   4. Any parameter or token used with `##`
 
-   3. anything followed by a `#`
+      - `##` concatenates tokens without evaluating them.
+      - The pasted result is *not* scanned recursively for macro calls.
 
-      `#` returns an atomic string token made from what follows it
+   5. any macro call token that has been "colored blue" by the evaluator.
 
-   4. anything connected by '##'
+   It is common that macros containing a `#` or a `##` will have an extra call
+   layer above them to cause the argument to be first substituted into a macro
+   where it will get evaluated. Large number of Eval calls are typically not needed
+   for this, rather what is needed is one layer above the layer with the `#` or `##`.
 
-      `##` does a concatenation and returns a new token.  This new token is not evaluated.
+** 3. Colored blue macro call tokens are taken as literal text.
+
+   A macro color token is the text of a macro call considered as a single token. The following is an example macro call token:
+
+   #+BEGIN_SRC c
+   name(1 ,a ,b)
+   #+END_SRC
+
+   During evaluation cpp keeps a list of macros that are actively being expanded.  This includes the macro that has been called, of course, and all the calls that it found during its depth traversal to get to the current point of evaluation. If it sees any calls to the macros on the active expand list, it marks the macro call token itself as unexpandable, and thus to be treated as literal text. It is said that the macro call token "has been colored blue".
+
+   Any subsequent appearance of a colored blue macro call token will be treated as literal text, no matter the context. This includes if the macro result was assigned to a variable, then that variable is evaluated later.  It includes if the macro call token or the expression it is in is sent to another macro, etc.  
+
+   Example:
+
+   #+BEGIN_SRC c
+   #define GROW(x) 1029 * GROW(x)
+   #define RESULT GROW(5)
+
+   SHOW(RESULT);        // â†’ 1029 * GROW(5)
+   SHOW(EVAL(RESULT));    // â†’ 1029 * GROW(5) again, never expands
+   #+END_SRC
 
-   2. Recursion has been designed out.
+   The macro call token `GROW(x)` which occurs in the body of a macro definition of the same name, gets colored blue, and thus is always treated as literal text. This happens even though an expression containing it is assigned to a variable, then that variable is evaluated in a totally different evaluation context.
 
-      Any macro call token that matches the name of the macro being evaluated is marked, and can never be called. It will forever more be treated as literal text. Note
-      the examples in the next section.
+  Apparently the designers of cpp do not want people to design recursive structures with cpp. However, there is a trick for getting around it using a confederate in place of the recursive call, then putting back the original call later, as shown in section 6.
       
 3. Example macros
 
       the evaluation moves right.
       
    2. ~EMPTY()~ is a macro, so it has no operands, so it is expanded. As it has no
-      definition, it expands to nothing. CPP then moves to the right.
+      body, it expands to nothing. CPP then moves to the right.
 
    3. ~(x)~ is not a macro so there is nothing to recur into, so it becomes ~(5)~.
 
index 35e8d81..af9405d 100644 (file)
@@ -1,15 +1,13 @@
 /*
+  See also 
+    https://github.com/18sg/uSHET/blob/master/lib/cpp_magic.h 
+    and tutorial at: http://jhnet.co.uk/articles/cpp_magic
 
-Provides:
+    documents in $REPO_HOME/developer/document🖉
 
-  COMMA, SEMICOLON, EXISTS, NOT_EXISTS, ,MATCH_RWR ,NOT_MATCH_RWR, BOOL, NOT, AND, OR, EQ, NOT_EQ, IF_ELSE
-
-1.
-  See also https://github.com/18sg/uSHET/blob/master/lib/cpp_magic.h 
-  and tutorial at: http://jhnet.co.uk/articles/cpp_magic
+1. Provides:
 
-  I could not have even thought about writing this without Jonathan Heathcote's
-  little tutorial.
+  COMMA, SEMICOLON, EXISTS, NOT_EXISTS, ,MATCH_RWR ,NOT_MATCH_RWR, BOOL, NOT, AND, OR, EQ, NOT_EQ, IF_ELSE
 
 2.
   These are the non-recursive extensions.  See cpp_ext_1 for the recursive extensions.
@@ -25,72 +23,6 @@ Provides:
 
   EQ comparisons apart from logic comparisons, must be registered in advance. They take the form of, _RWR_EQ__<x>__oo__<y>, note comments below.
 
-5. Evaluation 
-
-  1. cpp does not evaluate arguments that in the definition are attached to hash operator.
-
-       #define STR(x) #x   // x is not evaluated.
-       #define VAL(x) STR(x)  // x is evaluated recursively before STR is expanded
-
-     The same applies for '##'
-
-  2. cpp evaluates from left to right, for each macro it finds it does depth first revalution.
-
-  3. Due to the left to right evaluation a funny things happens.
-
-      #define EMPTY()
-      #define NEGATE(x) -x
-     
-      #define NOT_SO_FAST(x)  NEGATE EMPTY() (x)
-
-      What happens in left to right evaluation of NOT_SO_FAST(5)
-      1. first NEGATE will be evaluated, there is nothing to expand
-      2. EMPTY() is evaluated to nothing
-      3. (x) is evaluated to 5.
-      4. reached the right side, done result is:
-
-      ->  NEGATE (x)
-
-        > cat >test.c << EOF
-        #include <stdio.h>
-        int main(void){
-          printf("example_eval.c\n");
-
-          #define STR(x) #x
-
-          // `#x` no evaluation of x 
-          // `STR(x)` one left to right pass evaluation of `x` due to depth recursion
-          #define SHOW(x) printf(#x " â†’ %s\n", STR(x));
-
-          #define EMPTY()
-          #define NEGATE(x) -x
-          #define NOT_SO_FAST(x)  NEGATE EMPTY() (x)
-          SHOW(NOT_SO_FAST(5));
-
-          #define BE(x) x
-          SHOW(BE(NOT_SO_FAST(5)))
-        }
-        EOF
-
-        > gcc test.c
-        > ./a.out
-        example_eval.c
-        NOT_SO_FAST(5) â†’ NEGATE (5)
-        BE(NOT_SO_FAST(5)) â†’ -5
-        > 
-
-    Consider the lines:
-
-      #define BE(x) x
-      SHOW(BE(NOT_SO_FAST(5)))
-    
-    cpp left to right scan finds one macro to expand: BE(NOT_SO_FAST(5))
-    cpp depth first into that macro finds 5, which expands to -> 5
-    going up a level cpp tries: NOT_SO_FAST(5) -> NEGATE (5)
-    going up a level cpp tries: BE( NEGATE (5) ) ->  BE( -5 ) -> -5
-
-
-
 
 */
 
@@ -124,6 +56,19 @@ Constants
 #define _RWR_EQ__0__oo__0
 #define _RWR_EQ__1__oo__1
 
+/*===========================================================================
+Primitive Concatenation
+  Not due to elegance, as `##` is convenient, rather this is 
+  used to force evaluation of arguments before `##`.
+  There will be a recursive CAT of n things in cpp_ext_1.c
+===========================================================================*/
+
+#define _CAT2(a ,b) a ## b
+#define CAT2(a ,b) _CAT(a ,b)
+
+#define _CAT3(a ,b ,c) a ## b ## c
+#define CAT3(a ,b ,c) _CAT(a ,b ,c)
+
 /*===========================================================================
 Logic
 ===========================================================================*/
@@ -138,8 +83,10 @@ Logic
 
   //----------------------------------------
   // existence
+  //
+  // `##` prevents rewrite of _TWION_ in the _EXISTS_ITEM_1 macro, don't
+  // replace that with CAT!
 
-  // `##` prevents rewrite of _TWION_ in the _EXISTS_ITEM_1 macro
   #define _EXISTS_ITEM_2(x_item) _SECOND(x_item ,1) 
   #define _EXISTS_ITEM_1(x_item) _EXISTS_ITEM_2(_TWION_0##x_item)
 
@@ -200,17 +147,21 @@ Logic Connectors
   #define _NOT_EQ(x_item ,y_item) EXISTS(_RWR_EQ__##x_item##__oo__##y_item)
   #define NOT_EQ(x_item ,y_item) _NOT_EQ(x_item ,y_item)
 
-
 /*===========================================================================
   IF-ELSE construct.
   Usage: IF_ELSE(condition)(<true case>)(<false case>)
 
   A most amazing little macro. It has no dependencies on the other macros
   in this file, though many will be useful for setting (condition)
+
+  The seemingly extra layer prevents BOOL_(condition) from being pasted
+  with a ## which, if done, would prevent it from being evaluated.  Recall,
+  the first step in evaluation is a literal copy in of the arguments.
 ===========================================================================*/
 
   #define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
-  #define _IF_ELSE(condition)  _IF_##condition
+  #define _IF_ELSE(condition) __IF_ELSE(condition)
+  #define __IF_ELSE(condition)  _IF_##condition
   #define _IF_1(...)          __VA_ARGS__ _IF_1_ELSE
   #define _IF_0(...)                      _IF_0_ELSE
   #define _IF_1_ELSE(...)
index 0003f19..bcf2c63 100644 (file)
@@ -6,6 +6,7 @@
 #include "cpp_ext_0.c"
 
 int main(void){
+
   //--------------------------------------------------------------------------
   // Existence Checks (sanity anchor)
   //--------------------------------------------------------------------------
@@ -41,8 +42,6 @@ int main(void){
   SHOW( _OR(1 ,1) );   // rule missing â†’ NOT_MATCH â†’ 1
   printf("\n");
 
-
-
   //--------------------------------------------------------------------------
   // Equality
   //--------------------------------------------------------------------------
@@ -88,14 +87,54 @@ int main(void){
   SHOW( OR(1 ,0) );      // BOOL(1), BOOL(0) = 0,0 â†’ OR(0 ,0) = _OR(0 ,0) = 0
   printf("\n");
 
+  //--------------------------------------------------------------------------
+  // Compound Connectors
+  //--------------------------------------------------------------------------
+
+  // Double NOT
+  SHOW( NOT(NOT(0)) );     // â†’ NOT(1) â†’ _NOT(1) â†’ 0
+  SHOW( NOT(NOT(1)) );     // â†’ NOT(0) â†’ _NOT(0) â†’ 1
+
+  // Triple NOT
+  SHOW( NOT(NOT(NOT(0))) );  // â†’ NOT(0) â†’ _NOT(0) â†’ 1
+  SHOW( NOT(NOT(NOT(1))) );  // â†’ NOT(1) â†’ _NOT(1) â†’ 0
+  printf("\n");
+
+  // Nested AND
+  SHOW( AND(1 ,AND(1 ,0)) );  // AND(1 ,AND(1 ,0)) â†’ AND(1 ,0) â†’ _AND(1 ,0) = 0
+  SHOW( AND(1 ,AND(1 ,1)) );  // â†’ AND(1 ,1) â†’ _AND(1 ,1) = 1
+  printf("\n");
+
+  // Nested OR
+  SHOW( OR(0 ,OR(0 ,1)) );    // â†’ OR(0 ,1) â†’ _OR(0 ,1) = 1
+  SHOW( OR(0 ,OR(0 ,0)) );    // â†’ OR(0 ,0) â†’ _OR(0 ,0) = 0
+  printf("\n");
+
+  // Mixed nesting
+  SHOW( AND(NOT(0) ,1) );     // â†’ AND(1 ,1) â†’ _AND(1 ,1) = 1
+  SHOW( AND(NOT(1) ,1) );     // â†’ AND(0 ,1) â†’ _AND(0 ,1) = 0
+  SHOW( OR(NOT(1) ,1) );      // â†’ OR(0 ,1) â†’ _OR(0 ,1) = 1
+  SHOW( OR(NOT(0) ,0) );      // â†’ OR(1 ,0) â†’ _OR(1 ,0) = 1
+  printf("\n");
+
+  // Deep mix
+  SHOW( NOT(AND(1 ,NOT(1))) );  // AND(1 ,0) = _AND(1 ,0) = 0 â†’ NOT(0) = _NOT(0) = 1
+  SHOW( NOT(OR(0 ,NOT(0))) );   // OR(0 ,1) = _OR(0 ,1) = 1 â†’ NOT(1) = _NOT(1) = 0
+  printf("\n");
+
+  // Asymmetric nesting
+  SHOW( AND(OR(0 ,1) ,AND(1 ,1)) ); // OR(0 ,1) = 1, AND(1 ,1) = 1 â†’ AND(1 ,1) = 1
+  SHOW( OR(AND(1 ,0) ,AND(1 ,1)) ); // AND(1 ,0) = 0, AND(1 ,1) = 1 â†’ OR(0 ,1) = 1
+  printf("\n");
+
   return 0;
 }
 /*
-  2025-03-27T12:40:37Z[developer]
+  2025-03-27T12:59:39Z[developer]
   Thomas-developer@Stanley§/home/Thomas-masu/developer/N/developer/example§
   > gcc try_2_connectors.c 
 
-  2025-03-27T12:41:56Z[developer]
+  2025-03-27T13:00:11Z[developer]
   Thomas-developer@Stanley§/home/Thomas-masu/developer/N/developer/example§
   > ./a.out
   EXISTS_ITEM(x0) --> 0
@@ -148,8 +187,32 @@ int main(void){
   OR(0 ,0) --> 0
   OR(1 ,0) --> 1
 
+  NOT(NOT(0)) --> 0
+  NOT(NOT(1)) --> 1
+  NOT(NOT(NOT(0))) --> 1
+  NOT(NOT(NOT(1))) --> 0
+
+  AND(1 ,AND(1 ,0)) --> 0
+  AND(1 ,AND(1 ,1)) --> 1
 
-  2025-03-27T12:41:59Z[developer]
+  OR(0 ,OR(0 ,1)) --> 1
+  OR(0 ,OR(0 ,0)) --> 0
+
+  AND(NOT(0) ,1) --> 1
+  AND(NOT(1) ,1) --> 0
+  OR(NOT(1) ,1) --> 1
+  OR(NOT(0) ,0) --> 1
+
+  NOT(AND(1 ,NOT(1))) --> 1
+  NOT(OR(0 ,NOT(0))) --> 0
+
+  AND(OR(0 ,1) ,AND(1 ,1)) --> 1
+  OR(AND(1 ,0) ,AND(1 ,1)) --> 1
+
+
+  2025-03-27T13:00:12Z[developer]
   Thomas-developer@Stanley§/home/Thomas-masu/developer/N/developer/example§
   >
 */
+
+