~sebsite/hare-c

C lexer/parser/checker/compiler for Hare

1bf070a check: fix typo

~sebsite pushed to ~sebsite/hare-c git

2 days ago

90edd62 check: only recognize func in c99+

~sebsite pushed to ~sebsite/hare-c git

29 days ago

#hare-c

C parsing and checking library for Hare

A sprite in Scratch of a hare, named "Hare-c"

#Stuff in this repo

  • c - the c:: Hare module
  • cmd/cdecl - WIP-ish converter from C gibberish to readable English, and vice-versa. depends on madeline
  • cmd/crepl - C interpreter/REPL
  • cmd/hacc - C compiler. depends on qbe
  • cmd/hareconv - converter between C headers and Hare modules
  • docs - man pages and implementation documentation

#hi pls contribute

Send patches to ~sebsite/hare-c@lists.sr.ht (archive)

If you need help or have questions ping me on fedi or IRC or wherever :)

#Checklist

This checklist isn't exhaustive; I'm updating it as I go.

Legend:
 🗸  implemented
T   properly tested
X   runtime behavior; currently can't be tested

   basics
T🗸   comments
T🗸     block
T🗸     line
T🗸   backslashes at end of line
     declarations
T🗸     must declare at least one identifier
       specifiers
         type specifiers
T🗸         signed, unsigned
T🗸           parse
T🗸         void
T🗸           parse
T🗸           incomplete type; cannot be completed
T🗸         char
T🗸           parse
T🗸         short
T🗸           parse
T🗸         int
T🗸           parse
T🗸         long
T🗸           parse
T🗸         float
T🗸           parse
T🗸         double
T🗸           parse
           struct/union
T🗸           struct
T🗸             declarations
T🗸               named
T🗸                 parse
T🗸                 check
T🗸               anonymous
T🗸                 parse
T🗸                 check
T🗸               definitions
T🗸                 parse
T🗸                 check
T🗸               forward
T🗸                 parse
T🗸                 check
 🗸           union
 🗸             declarations
 🗸               named
T🗸                 parse
 🗸                 check
 🗸               anonymous
T🗸                 parse
 🗸                 check
 🗸               definitions
T🗸                 parse
 🗸                 check
 🗸               forward
T🗸                 parse
 🗸                 check
             bit-fields
T🗸             parse
T🗸               named
T🗸               anonymous
               check
                 can't take address
                 width requirements
                   integer constant expression
                   non-negative
                     may be zero
                   less than or equal to width of type
                 width must be integer constant expression
                 width must be non-negative
                 may or may not have declarator if width > 0
                 zero width
                   declarator must be absent
                   causes following bit-fields to not be packed into the previous bit-field's unit
                 adjacent bit-fields are packed into the same union
                   true for both structs and unions
                 int is signed
                 type can't be atomic
T🗸           incomplete types disallowed
T🗸           must contain at least one named member
T🗸           variably modified member types disallowed
T🗸           tag inserted into scope immediately after it's declared
             tag is always declared when not used as type specifier
T🗸           when used as type specifier, tag is only declared if no other declaration is visible
T🗸           duplicate fields disallowed
T🗸             fields outside of nested structs/unions
T🗸             including fields from nested structs/unions
T🗸           fields may not have function type
             tag is inserted into scope immediately after declared
               definition for same struct/union can't appear within struct/union definition body
           enum
T🗸           declarations
T🗸             named
T🗸             anonymous
T🗸             definitions
T🗸             forward disallowed
T🗸           members inserted into scope
T🗸             parse
T🗸             no linkage
T🗸               can't be redeclared
T🗸             not lvalue
T🗸               can't take address
T🗸             inserted into scope immediately after identifier
T🗸           tag inserted into scope immediately after definition ends
T🗸           compatible type
T🗸             implicit enumerated type
T🗸               type is unsigned int if all members are representable
T🗸               otherwise, type is int
T🗸             enumeration member type is int
T🗸           enum type is compatible with, but not equivalent to, enumerated type
 🗸           error if any value would overflow enumeration member type
 🗸             with initializer
 🗸             without initializer
T🗸           duplicate fields disallowed
T🗸             enum member redeclares existing non-enum declaration
T🗸             enum member redeclares existing different enum declaration
T🗸             enum member redeclares member of same enum
T🗸           enum type is incomplete until definition ends
T🗸         typedefs
T🗸           parse
T🗸           inserted into scope
T🗸           file scope
T🗸             disallowed for variably modified types
T🗸             allowed otherwise
T🗸           block scope
T🗸             variably modified types allowed
T🗸             all other types allowed
T🗸       storage-class specifiers
T🗸         auto
T🗸           parse
T🗸         static
T🗸           parse
T🗸         register
T🗸           parse
T🗸           forbids taking address
T🗸             explicitly
T🗸             implicitly (e.g. array -> pointer conversion)
T🗸             also applies to derivative lvalues
T🗸         extern
T🗸           parse
T🗸           defaults to external linkage
T🗸           uses linkage of identifier visible in scope when it's internal or external
T🗸         typedef
T🗸           parse
T🗸           inserts type into scope
T🗸           can't have initializer
T🗸         at most one storage-class specifier per declaration (sans thread_local)
T🗸         duplicates disallowed
T🗸       duplicate specifiers
T🗸         disallowed for type specifiers
T🗸         allowed for other specifiers
T🗸       disallow incompatible
T🗸         type specifiers
T🗸         other specifiers
T🗸     qualifiers
T🗸       allowed in any order
T🗸       duplicates allowed
       declarators
T🗸       pointer
         function
           parameters
T🗸           named
T🗸           anonymous
X🗸           arrays converted to (qualified) pointers
X🗸             still checked as arrays
X🗸           functions converted to function pointers
T🗸           may not declare more than one (non-tag) identifier
T🗸           may not be initialized
T🗸           without storage-class specifier
T🗸             defaults to implicit auto
T🗸           register storage-class specifier
T🗸             parse
T🗸             declares with register storage
T🗸             ignored for non-definitions
T🗸               may be different in compatible types
T🗸             applies even when other declarations' parameters have different/absent storage-class specifiers
T🗸           all other specifiers disallowed
 🗸           empty parameter list
 🗸             designates unspecified number of parameters
 🗸             isn't compatible with variadic function types
T🗸           treated as unqualified when taking composite type
             inserted into scope immediately after declared
               can be used in declarators of subsequent parameters
               can be used by previous parameters in k&r declarations
T🗸         return type requirements
T🗸           may not return array
T🗸           may not return function
T🗸         warn when function returning complete type doesn't have return statement
T🗸           except for main function in hosted environment
T🗸           all other functions
T🗸         storage-class specifier must be static or extern (explicit or otherwise)
T🗸         error out when explicitly given qualifiers (via typedef)
T🗸         implicit const qualifier
T🗸         qualifiers discarded for return type
T🗸       array
T🗸         without qualifiers
T🗸         qualifiers apply to element type
T🗸           don't apply to array itself before c23
T🗸         ...except as inner-most declarator of function parameter
T🗸           different from qualifying an array typedef or typeof
T🗸         qualifiers may only be in square brackets in inner-most declarator of function parameter
T🗸         if present, known size must be greater than zero
T🗸         element type must be complete object type
T🗸       identifier only
T🗸       parenthesized
T🗸       type names
T🗸         with declarator
T🗸         without declarator
T🗸     scopes
T🗸       file
T🗸         parse
T🗸         auto/register specifiers disallowed
T🗸         default storage
T🗸           extern for functions
T🗸           external linkage for objects
T🗸       block
T🗸         parse
T🗸         default storage
T🗸           static for functions (TODO: double check this)
T🗸           auto for objects
T🗸       duplicates
T🗸         file scope
T🗸           allowed for compatible declarations
T🗸             composite type
T🗸             compatible specifiers
T🗸           disallowed for incompatible declarations
T🗸           at most one definition allowed
T🗸         block scope
T🗸           disallowed within same scope
T🗸           shadowing in nested scopes
T🗸             shadowing block scoped declarations
T🗸             shadowing file scoped declarations
T🗸         struct/union/enum tags must always refer to same type
T🗸       unique namespaces
T🗸         identifiers
T🗸         struct/union/enum
T🗸         goto labels
T🗸           function scope
T🗸     internally-linked declarations must be complete by end of translation unit
T🗸     translation unit must not be empty (static assertions and empty declarations are ok)
       internally-linked declaration must be initialized if used
         except sizeof
         except alignof
         all other expressions
T🗸     can't declare an object with type void
     expressions
 🗸     constants and literals
T🗸       numbers
T🗸         preprocessing numbers
T🗸         integer constants
T🗸           bases
T🗸             decimal
T🗸             hex
T🗸             octal
T🗸           suffixes
T🗸             u
T🗸             l
T🗸           parse value
T🗸         floating constants
T🗸           decimal
T🗸           exponent
T🗸           f suffix
T🗸           parse value
T🗸       character constants
T🗸         plain
T🗸         L prefix
 🗸       string literals
T🗸         plain
T🗸         L prefix
T🗸         string concatenation
T🗸           works for same-prefix strings
T🗸           disallowed for strings with different prefixes
T🗸         trigraph-like character sequences are escaped when unparsed (prior to c23)
 🗸         may be invalid utf-8
       sizeof
T🗸       parse
T🗸         type
T🗸         expression
T🗸           unambiguous expressions
T🗸           unparenthesized compound literal
X🗸       operand isn't evaluated if not variably modified
X🗸       operand is evaluated if variably modified
         operand must not be bit-field
T🗸       operand must be complete object
T🗸     array indexing
T🗸       parse
T🗸     struct/union field accessing
T🗸       forms (parse)
T🗸         a.b
T🗸         a->b
T🗸       check
T🗸       disallowed when object has atomic type
T🗸     unary postfix
T🗸       parse
T🗸     unary prefix
T🗸       parse
T🗸       &
T🗸         compile-time
T🗸         runtime
T🗸         operand is lvalue
T🗸         operand is function designator
T🗸         operand is unary *
T🗸           allowed when explicit
T🗸           allowed when implicit from array indexing
X🗸           neither & nor * is evaluated
T🗸           constraints still apply
T🗸         everything else disallowed
T🗸     binary
T🗸       parse
T🗸     assignment
T🗸       parse
T🗸       simple assignment
T🗸       compound assignment
X🗸         operands are only evaluated once
X🗸         computed value is computed equivalently to binary expression
T🗸         result of binary expression must be assignable to first operand's type
T🗸           second operand can't be a pointer for addition or subtraction
T🗸           everything else is fine
T🗸       first operand must be a mutable lvalue
T🗸       first operand must not be an array
T🗸     parenthesized
T🗸     ternary
T🗸     operator precedence
       conversion
T🗸       explicit (cast expressions)
T🗸         parse
T🗸         extends implicit type conversion rules
T🗸         integer -> pointer
T🗸         pointer -> integer
T🗸         pointer -> pointer
T🗸           object -> object
T🗸           function -> function
T🗸           no limitations for qualifiers
T🗸         any -> void
T🗸         everything else disallowed (except object <-> function; see extensions)
         implicit
           integer promotion
T🗸           non-bit-fields
             bit-fields
               width taken into account
               bit-precise integers don't change, regardless of width
T🗸         lvalue conversion
 🗸         types
T🗸           integer -> float
T🗸           float -> float
T🗸           integer -> integer
T🗸           float -> integer
T🗸           pointer -> bool
T🗸           array -> pointer
T🗸           function -> pointer
T🗸           pointer -> pointer
T🗸             pointer -> compatible pointer
T🗸             object pointer -> void pointer
T🗸             void pointer -> object pointer
T🗸             can't implicitly convert to any other pointer
T🗸             qualifiers may be added
T🗸             qualifiers may not be removed
 🗸           struct/union -> same struct/union
T🗸             tagged
 🗸             untagged
 🗸               until c23
 🗸               since c23
T🗸           integer -> pointer
T🗸             allowed when converting from integer constant 0
T🗸             disallowed otherwise
T🗸           nullptr -> pointer
T🗸           nullptr -> bool
T🗸           nullptr -> nullptr
T🗸           null pointer constant -> pointer
T🗸           null pointer constant -> nullptr
T🗸           everything else disallowed
T🗸     function call
T🗸       parse
T🗸       check
T🗸       arguments must have complete type
     constant expressions
 🗸     disallowed (sub-)expressions
T🗸       assignment
T🗸       increment/decrement
T🗸       array indexing
T🗸       arrow expression (->)
T🗸       unary *
T🗸         including as operand of unary &
T🗸       function call
T🗸       comma expression
T🗸       __builtin_va_arg
 🗸       __builtin_va_copy
 🗸       __builtin_va_end
 🗸       __builtin_va_start
       must evaluate to value which is representable in its type
       struct/union field access
 🗸       allowed if constexpr
         disallowed if not constexpr
         result is also constexpr
         result is named constant if struct/union object is named constant
 🗸     enum constant
 🗸     pre-defined constant
 🗸       true
 🗸       false
 🗸       nullptr
 🗸     constexpr object
       integer constant expressions
 🗸       top-most expression may be anything not generally disallowed, but see limitations
 🗸       result must be integer type
         limitations for operands
T🗸         integer constant
 🗸         named constant with integer type
           constexpr compound literal with integer type
             non-constexpr compound literal disallowed
T🗸         character constant
T🗸         translation-time sizeof
T🗸         alignof
T🗸         __builtin_offsetof
           cast
             additional expressions allowed as immediate operand
 🗸             floating-point constant
 🗸             named constant with arithmetic type
               constexpr compound literal with arithmetic type
                 non-constexpr compound literal disallowed
           everything else disallowed, unless not evaluated (sizeof/alignof)
T🗸           including pre-defined constants like true and false
             sizeof on variably modified type disallowed (since it's evaluated)
       arithmetic constant expressions
         TODO
T🗸     constant expression in initializer
T🗸     null pointer constant
T🗸       integer constant expression which evaluates to zero
T🗸       ...casted to void *
T🗸         unqualified
T🗸         qualified
T🗸         void can't be qualified
T🗸       doesn't otherwise affect type conversion rules
 🗸     integer promotion
 🗸       unsigned -> unsigned
 🗸       unsigned -> signed
 🗸       signed -> unsigned
 🗸       signed -> signed
 🗸       char's signedness is taken into account
 🗸     additional errors
 🗸       don't error when not evaluated
T🗸         including because of short-circuiting
 🗸         expression which isn't evaluated need not be constant
 🗸       division by zero
T🗸         integer division
T🗸         integer modulus division
 🗸         floating-point is ok (TODO?)
T🗸       bit-shift right operand exceeds integer width
T🗸       signed integer overflow
T🗸         addition
T🗸         subtraction
T🗸         multiplication
T🗸         division
T🗸         modulo
T🗸         left shift
T🗸         negation
 🗸     TODO: write more shit here i guess
     statements
T🗸     goto
T🗸       parse
T🗸       jump to any label within function
T🗸       disallow undefined labels
T🗸       may not jump from outside scope of VMT to inside scope
T🗸     compound
T🗸       parse
       labelled
T🗸       goto label
T🗸         parse
T🗸         unique per-function
         case
T🗸         parse
T🗸         must evaluate to integer constant
           case type is converted to type of controlling expression
           warn if constant value is truncated
T🗸         must only appear in switch body
T🗸           direct descendant
T🗸           within another block
T🗸           disallowed everywhere else
T🗸           only visible to inner-most switch body
T🗸         each case in switch body is unique
T🗸         ...but nested switch statements may contain duplicates
T🗸       default
T🗸         parse
T🗸         must only appear in switch body
T🗸           direct descendant
T🗸           within another block
T🗸           disallowed everywhere else
T🗸           only visible to inner-most switch body
T🗸         at most one per switch body
T🗸         ...but nested switch statements may contain their own
T🗸       labelled statements may themselves be labelled
T🗸     empty
T🗸       parse
T🗸     if
T🗸       parse
T🗸       condition must be scalar
T🗸     while
T🗸       parse
T🗸       condition must be scalar
T🗸     do-while
T🗸       parse
T🗸       condition must be scalar
T🗸     for
T🗸       parse
T🗸       condition must be scalar (if present)
T🗸       initializer, condition, and afterthought may all be omitted
T🗸     switch
T🗸       parse
T🗸       value must be integer
X🗸       value is promoted
T🗸       no VMTs not in scope of switch statement may be in scope of any cases
T🗸     break
T🗸       parse
T🗸       allowed in loop
T🗸       allowed in switch statement
T🗸       disallowed everywhere else
X🗸       causes jump to outside whatever it applies to
T🗸         within check
X🗸         for-loop afterthought isn't evaluated
T🗸     continue
T🗸       parse
T🗸       allowed in loop
T🗸       disallowed everywhere else
T🗸       causes jump to end of loop it applies to
T🗸         within check
X🗸         so for-loop afterthought is evaluated, then condition
T🗸     return
T🗸       parse
T🗸       type of return value must be convertible to function return type
T🗸       function with void result type can't return an expression
T🗸         non-void expression
T🗸         void expression
T🗸       returning without expression from function with non-void result type
T🗸         warn before c99
T🗸         error after c99
T🗸       warn when used in noreturn function
T🗸     expression statements
T🗸       parse
T🗸       implicitly convert to void
T🗸     bindings
T🗸       TODO: write more shit here (stuff handled in fromabinding)
T🗸       enters scope before initializer is checked
 🗸   initializers
T🗸     parse
T🗸     non-compound
T🗸       without braces
T🗸       may be surrounded by braces
T🗸         when single scalar object
T🗸         when using string literal to initialize array
 🗸     compound initializers
T🗸       parse
T🗸       check
T🗸         array literals
T🗸           complete array type
T🗸           incomplete array type
T🗸             set length to length of literal
T🗸         struct literals
T🗸         union literals
T🗸       can be nested
 🗸       must always consist of constant expressions prior to c99
 🗸         braced scalar initializer need not be constant
T🗸     must be constant expression for objects with non-automatic storage duration
T🗸     may be any expression otherwise (except for compound initializer prior to c99)
T🗸     string literal may initialize array
T🗸       allowed when array length >= sizeof(string) - 1
T🗸       disallowed when array length < sizeof(string) - 1
T🗸       for incomplete array type, length defaults to sizeof(string)
X🗸       excess array elements are initialized to zero (nul)
T🗸   variadic functions
T🗸   function definitions
T🗸     declaration inserted into scope
T🗸       before body is parsed
T🗸       before body is checked
T🗸     declarator must be function
T🗸       non-pointer accepted
T🗸       pointer rejected
T🗸       typedef rejected
T🗸     without arguments (void)
T🗸     with named arguments
T🗸     argument types must be complete (sans (void) case)
T🗸     return type must be either complete type or void (in addition to other requirements)
T🗸   identifiers
T🗸   no such thing as an invalid token
     main function
 🗸     no requirements or restrictions imposed in freestanding environment
       hosted environment
 🗸       warn; don't error
         if __asm__("main") is given, no restrictions on the function's identifier are imposed
T🗸       must be function
 🗸       must have strictly conforming prototype
 🗸         (void)
T🗸         (int, char **)
T🗸         ...or equivalent, expanding typedefs and performing usual parameter conversions
 🗸         all other forms rejected
 🗸       must have external linkage
T🗸       checked even if no definition is present
   preprocessor
T🗸   macro definitions
T🗸     #define
T🗸       variable-like
T🗸         creates macro definition
T🗸       function-like
T🗸         creates macro definition
T🗸         duplicate parameters disallowed
T🗸       shadowing keywords
T🗸     undef
T🗸       shadowing keywords
T🗸       other identifiers
     macro substitution
T🗸     pre-defined macros
T🗸        standard
T🗸          constant
T🗸            __STDC__
T🗸            __STDC_HOSTED__
T🗸          special
T🗸            __FILE__
T🗸            __LINE__
X🗸            __DATE__
X🗸            __TIME__
T🗸        non-standard
T🗸          constant
T🗸            target-independent
T🗸              __HARE_C__
T🗸              __ORDER_LITTLE_ENDIAN__
T🗸              __ORDER_BIG_ENDIAN__
T🗸              __ORDER_PDP_ENDIAN__
T🗸              __STRICT_ANSI__
T🗸            target-dependent
T🗸              __BYTE_ORDER__
T🗸          special
T🗸            __BASE_FILE__
T🗸            __COUNTER__
T🗸            __INCLUDE_LEVEL__
T🗸            __FILE_NAME__
X🗸            __TIMESTAMP__
X🗸            __VERSION__
       operators
         # (%:)
 🗸         without whitespace
           whitespace is preserved, but collapsed into a single space
 🗸       ## (%:%:)
T🗸         with macro arguments
T🗸           only token closest to ## is concatenated
T🗸           when argument expands to no tokens, replace with placemarker
T🗸         with other tokens
 🗸         constructs new token
T🗸           non-preprocessing tokens
 🗸           constructed token won't be used for preprocessing (i.e. no # or ##)
 🗸     # has higher precedence than ##
T🗸     object-like
       function-like
T🗸       works when followed by left paren
T🗸       doesn't work when not followed by left paren
         parameters
T🗸         expand non-recursively
T🗸           expands macros
T🗸           everything else
T🗸         don't expand recursively
T🗸           from parameters
T🗸           from macros
           warn in c90 when argument expands to no tokens
T🗸     recursive
T🗸       macros being expanded can't be expanded again
T🗸         macro can't expand itself
T🗸         macro can't be expanded from within nested expansion
T🗸       everything else expands
     #include
T🗸     system headers
T🗸     non-system headers
T🗸       header exists
T🗸       header doesn't exist; fallback to system header
T🗸         header name doesn't have angle brackets
T🗸         header name has angle brackets
T🗸     macro expansion
T🗸       expands into system header
T🗸       expands into non-system header
T🗸       multiple macros may expand into one resource
T🗸     <this> is lexed as a system header string literal
T🗸       true in #include
T🗸       false everywhere else (prior to c23)
       warn if header name contains any of ', \, ", //, or /*
 🗸   conditional
T🗸     #if, #endif
T🗸     #else
T🗸     #elif
T🗸     defined
T🗸       non-parenthesized identifier
T🗸       paranthesized identifier
T🗸       error out when neither of the above forms is matched
T🗸     #ifdef
T🗸     #ifndef
 🗸     evaluation
T🗸       integer constant expressions
 🗸       other expressions disallowed
 🗸       keywords are treated as ordinary identifiers
T🗸       all arithmetic has intmax_t range
T🗸   #error
T🗸     errors out
X🗸     error message uses all tokens
X🗸     macros aren't expanded
T🗸   #line
T🗸     only change line number
T🗸     also change filename
T🗸     macros are expanded
T🗸     line number may not exceed 2147483647
   legacy (c90)
T🗸   trigraphs
     k&r-style functions
 🗸     empty parameter list
       non-empty parameter list without types
         TODO: allowed in c90 when k&r declarations aren't present?
       k&r-style parameter declarations
         parse
         only allowed in definitions
         every binding must correspond to a parameter
         allowed when parameter list doesn't have types
           empty parameter list
           non-empty parameter list without types
         disallowed when parameter list has types
           void
           more than zero parameters
         TODO: are duplicates allowed?
         declarations can reference parameters which come later (TODO: do they need to be declared first?)
         function type is unchanged (TODO: allowed when another declaration has types in parameter list?)
         parameter bindings are given the appropriate type within the function's scope
       warns
     implicit int
 🗸     implicit function declarations
 🗸       extern int ()
 🗸       happens when first operand of a call expression is an undeclared identifier
 🗸         unparenthesized
 🗸         can't be parenthesized
 🗸       warns
       k&r parameter declarations may be omitted
         defaults to int
         warns
       type defaults to int when no type specifiers are present
         locations
 🗸         file scope
 🗸           with storage-class specifier or type qualifier(s)
 🗸           without storage-class specifier or type qualifier(s)
 🗸           declarations
 🗸           function definitions
 🗸         block scope
 🗸           storage-class specifier or type qualifier(s) must be present
           k&r parameter
             storage-class specifier or type qualifier(s) must be present
 🗸         function parameter
 🗸           storage-class specifier or type qualifier(s) must be present
 🗸         struct/union field
 🗸           type qualifier(s) must be present
 🗸           without bit-field
 🗸           with bit-field
 🗸         type name
 🗸           type qualifier(s) must be present
 🗸       when ambiguous, identifier which is a typedef is treated as a type specifier
         warns
     declaration may declare nothing
       locations
         file scope
         block scope
         k&r parameter
         struct/union
           anonymous structs/unions
           other types
       warns
 🗸   implicit conversion
 🗸     pointer->integer
 🗸     integer->pointer
T🗸   pragma
T🗸     #pragma
T🗸     _Pragma
T🗸     STDC
T🗸       accept standardized
T🗸         FP_CONTRACT
T🗸         FENV_ACCESS
T🗸         CX_LIMITED_RANGE
T🗸       reject unstandardized
T🗸     implementation-defined
T🗸       accepted; ignored
T🗸     backported to c90
T🗸 c95
T🗸   digraphs
T🗸   __STDC_VERSION__
   c99
     VLAs
T🗸     parse
T🗸     initialization
T🗸       can't be initialized by non-empty initializer
T🗸       can be initialized by empty initializer
       TODO: [*]
     hex float literals
T🗸     prefix / base
       parse value
T🗸     exponent
       warn if value can't be losslessly represented in its type
     universal character names
T🗸     in char/string literals
T🗸       parse
T🗸     in identifiers
T🗸       parse
T🗸       c99: conform to annex D (different from c11 annex D)
T🗸       c11, c17: conform to annex D (different from c99 annex D)
T🗸       c23: follow same rules as regular unicode codepoints in identifiers
T🗸     disallowed: (<0xa0 && !='$' && !='@' && !='`') || (>=0xd800 && <0xe000)
       preserved when stringizing
T🗸   initializer designators
T🗸     array
T🗸     struct
     declaration as for-loop initializer
T🗸     parse
T🗸     must be auto or register
T🗸       default is auto
       must not declare new struct/union tag
         tag may still be declared within expression
 🗸     enter scope immediately after declared
T🗸   specifiers
T🗸     type specifiers
T🗸       _Complex
T🗸       _Imaginary
T🗸       long double
T🗸       long long
       inline
T🗸       parse
T🗸       may appear more than once
T🗸       can't be used outside of function declaration
T🗸       applies to function declaration
T🗸       if inline is used on any declaration, there must be a definition
 🗸       internally-linked
 🗸         can be used on any declaration; no change in behavior
         externally-linked; inline definitions
 🗸         function becomes inline definition if all file scope declarations use inline
 🗸         ...and none explicitly use extern
 🗸         otherwise no change in behavior; function isn't an inline definition
 🗸         inline definition doesn't provide external definition
           constraints
             may not define a modifiable object with static or thread duration
             may not use identifier with internal linkage
T🗸     static array qualifier
T🗸       allowed in function parameter
T🗸       disallowed everywhere else
T🗸       length must be provided
T🗸   restrict
T🗸     parse
T🗸     restrictions on use (pun intended i think actually no it wasn't i lied)
T🗸       allowed on pointer to object type
T🗸       allowed on array of allowed type
T🗸       disallowed on non-array non-pointer types
T🗸       disallowed on pointer to function type
T🗸       disallowed on array of disallowed type
T🗸   variadic macros
T🗸     use of ... in definition
T🗸     __VA_ARGS__
T🗸       allowed in variadic macro definition
T🗸       disallowed everywhere else
T🗸     argument list must be preceded by comma
T🗸   intermixing declarations and statements in compound statements
T🗸   literal suffixes
T🗸     ll int literal suffix
T🗸     l float literal suffix
T🗸   __func__
T🗸     parse
T🗸     resolves to name of current function
T🗸     can't be redeclared at top-level
T🗸     has type 'const char []'
 🗸   implicit return 0 from main
T🗸     for hosted targets
 🗸     not for freestanding targets
     incomplete array struct member
T🗸     allowed when it's the final member and other members are present
T🗸     disallowed otherwise
T🗸     not allowed in union
T🗸     struct type is still complete; size is as though incomplete array wasn't present
T🗸     but struct type can't be use in other structs or arrays
T🗸       including if it's (recursively) a member of a union
       initialization in struct literal
 🗸       can be initialized when object has static storage duration
         can't be initialized otherwise
T🗸   pre-defined macros
T🗸     __STDC_ISO_10646__
     compound initializers/literals
T🗸     compound literals
T🗸       parse
T🗸       check
T🗸       type can't be VLA
T🗸     designators
T🗸       parse
T🗸         none
T🗸         array
T🗸         struct
T🗸       check
T🗸         none
T🗸         array
T🗸         struct
T🗸     multiple designators per object element may be given
T🗸     designators may be combined
       duplicate designators
 🗸       final object overwrites all previous objects
         warns
       warn when multiple designators for the same union are given
         excluding embedded structs
   c11
     specifiers
T🗸     _Thread_local
T🗸       parse
T🗸       may not appear alongside auto or register
T🗸       may not appear in block scope when no storage-class specifiers are present
T🗸       may not be used on function declaration
T🗸       must appear on all declarations of an object
       _Noreturn
T🗸       parse
T🗸       may appear more than once
         applies to function declaration
T🗸       can't be used outside of function declaration
         information persists in check
T🗸       warn when return type isn't void
       _Alignas
 🗸       expression
 🗸       type
         duplicates permitted; most strict alignment used
         invalid uses
T🗸         alongside typedef/register specifier
           on function
           on bit-field
         must not specify less strict alignment than default
       _Atomic
         as specifier
           parse
           type in parens must not be qualified
           type in parens must not be array or function
           type in parens must not be atomic
 🗸       as qualifier
 🗸         parse
T🗸   _Static_assert
T🗸     parse
T🗸       top-level
T🗸       within function
T🗸       within struct/union
T🗸     eval
T🗸       top-level
T🗸       within function
T🗸       within struct/union
T🗸       errors out when condition is false
T🗸   _Alignof
T🗸     parse
X🗸     operand isn't evaluated, even if variably modified
T🗸     operand must be complete object
T🗸   _Generic
T🗸     parse
T🗸     controlling expression conversions
T🗸       lvalue conversion
T🗸       array -> pointer
T🗸       function -> function pointer
T🗸     must match exactly one case
T🗸       at most one compatible case
T🗸       at most one default case allowed
T🗸       if no compatible case, default case must be present
T🗸     each non-default case must specify a non-variably-modified object type
T🗸   prefixes
T🗸     character constants
T🗸       u8-
T🗸       u-
T🗸       U-
T🗸     string literals
T🗸       u-
T🗸       U-
T🗸   nested anonymous structs/unions
T🗸   pre-defined macros
T🗸     __STDC_IEC_559__
T🗸     __STDC_UTF_16__
T🗸     __STDC_UTF_32__
   c23
T🗸   unicode
T🗸     identifiers (without universal character names)
T🗸     universal character names must be less than U+110000
     specifiers
 🗸     _BitInt
T🗸       signed
T🗸         parse
T🗸       unsigned
T🗸         parse
T🗸       wb int literal suffix
T🗸       operand must be integer constant expression
T🗸       signed width must be >= 2
T🗸       unsigned width must be >= 1
T🗸       doesn't undergo integer promotion
 🗸       has rank below equivalently sized basic integer type
 🗸       otherwise ranked based on width of both types
       typeof
T🗸       qualified (typeof)
T🗸       unqualified (typeof_unqual)
T🗸       with expression argument
T🗸       with type name argument
X🗸       operand isn't evaluated if not variably modified
         operand is evaluated if variably modified
         operand must not be bit-field
T🗸     decimal types
T🗸       _Decimal32
T🗸         type specifier
T🗸         df suffix
T🗸       _Decimal64
T🗸         type specifier
T🗸         dd suffix
T🗸       _Decimal128
T🗸         type specifier
T🗸         dl suffix
       constexpr
 🗸       parse
         check
 🗸         type
T🗸           implicitly qualified as const
T🗸           must be object (not function)
T🗸           must not be variably modified
 🗸           constraints
T🗸             must not be atomic
T🗸             must not be volatile or restrict qualified
 🗸             constraints apply recursively to subtypes
           initializer
T🗸           must be provided
T🗸           must be constant expression
             constraints
               value must be exactly representable in type
T🗸               integer
                 floating-point type
                   binary
                     real
                     imaginary
                     complex
                   decimal
T🗸             pointer initializer must be null pointer
T🗸             integer initializer must be integer constant expression
 🗸             arithmetic initializer must be arithmetic constant expression
T🗸             real float initializer must have integer or real float type
T🗸             imaginary float initializer must have imaginary float type
T🗸             decimal float initializer must have decimal float type
               constraints apply recursively to subobjects
     preprocessor
       #embed
         system embeds
         non-system embeds
           embed exists
           embed doesn't exist; fallback to system header
             embed name doesn't have angle brackets
             embed name has angle brackets
         parameters
           standard
             if_empty
             limit
             prefix
             suffix
           vendor-specific
             gnu::offset
             warn for other non-standard parameters
           duplicates not permitted
           leading+trailing underscores permitted
 🗸       macro expansion
 🗸         expands into system embed
 🗸         expands into non-system embed
 🗸         multiple macros may expand into one resource
 🗸         only expand if the unexpanded tokens have a syntax error
 🗸       <this> is lexed as a system header string literal
         `defined` token prohibited
         warn if header name contains any of ', \, ", //, or /*
       #warning
         actually warns
 🗸       warning message uses all tokens
 🗸       macros aren't expanded
       __VA_OPT__
         allowed in variadic macro definition
T🗸       disallowed everywhere else
T🗸     comma before variadic macro arguments can be omitted when there's no argument list
T🗸     #elifdef, #elifndef
       standard pragmas
         FENV_ROUND
 🗸         value must be direction
           eval
           information persists in check
         FENV_DEC_ROUND
 🗸         value must be dec-direction
           eval
           information persists in check
T🗸   static_assert without reason
T🗸   type inference with auto
T🗸     parse
T🗸     infers type
T🗸     initializer must be provided
T🗸     compound initializer disallowed
T🗸     array/function initializer is converted to pointer
T🗸     declaration inserted into scope immediately after declared, but can't be used
T🗸     can't be used on function definition
T🗸     must have exactly one declarator
T🗸     must be direct declarator
T🗸     can't be used alongside other storage-class specifiers
T🗸     can be used alongside qualifiers
T🗸     can be followed by attributes
T🗸     only usable on bindings
T🗸       not function parameters
T🗸       not struct/union fields
T🗸       not type names
T🗸   nullptr
T🗸   labels
T🗸     labelled declarations
T🗸     at end of compound statement
T🗸   binary int literals
T🗸     parse value
T🗸   function declarations
T🗸     variadic function without parameters
T🗸     parameters in function definition need not be named
     attributes
       locations
T🗸       declaration
T🗸         top-level
T🗸           bindings
T🗸           function definitions
T🗸           as a declaration consisting of only attributes and nothing else
T🗸         within compound body
T🗸         for-loop initializer
T🗸         function parameter
T🗸         struct/union fields
T🗸       binding
T🗸       base type
T🗸       declarator
T🗸         array
T🗸         function
T🗸         pointer
T🗸         with identifier
T🗸         without identifier
         struct/union/enum declaration
T🗸         parse
           allowed when defining struct/union/enum
           allowed in tag declaration where struct/union doesn't act as type specifier
             doesn't apply to enums since they can't be forward declared
           disallowed otherwise
T🗸       enum field
T🗸       statement
       standard
         attributes
           [[noreturn]], [[_Noreturn]]
             disallow argument
             applicable to
               function
               nothing else
             behaves equivalently to _Noreturn
           [[deprecated]]
             may have optional argument
             applicable to
               struct/union declaration
               typedef name
               object
               struct/union member
               function
               enum
               enum member
               nothing else
             warn when name is used
           [[fallthrough]]
             disallow argument
             applicable to
               lone attribute declaration
               next encountered statement must have case or default label
               if within iteration statement, next statement must also be within said iteration statement
           [[maybe_unused]]
             disallow argument
             applicable to
               struct/union declaration
               typedef name
               object
               struct/union member
               function
               enum
               enum member
               label
               nothing else
           [[nodiscard]]
             may have optional argument
             applicable to
               function declaration
               struct/union definition
               enum definition
               nothing else
             warn when value discarded
           [[reproducible]]
             disallow argument
             applicable to
               function declarator
               type specifier with function type
             warn when returns void
           [[unsequenced]]
             disallow argument
             applicable to
               function declarator
               type specifier with function type
             warn when returns void
T🗸       __has_c_attribute => 202311L
       non-standard
         warn
T🗸       __has_c_attribute => 0L
T🗸     leading+trailing underscores permitted
T🗸     keywords are treated as identifiers
T🗸     with prefix
T🗸     without prefix
T🗸     without arguments
T🗸     with arguments
T🗸       including balanced tokens
T🗸     multiple attribute lists are combined
T🗸     multiple attributes in [[brackets]]
T🗸   u8- string literals
T🗸   empty initializers
T🗸     parse
T🗸     scalar types
T🗸     array types
T🗸       allowed for complete array types
T🗸       disallowed for incomplete array types
T🗸         as initializer
T🗸         in compound literal
T🗸     struct types
T🗸     union types
T🗸   ' as separator
T🗸     int literals
T🗸     float literals
T🗸   empty declarations
     pre-defined macros
       keyword aliases
 🗸       alignas
T🗸       alignof
T🗸       bool
T🗸       true
T🗸         defined
T🗸         expands to _Bool value
T🗸       false
T🗸         defined
T🗸         expands to _Bool value
T🗸       static_assert
T🗸       thread_local
         when equivalent keyword is re-defined, alias macro doesn't expand said definition (TODO: maybe just don't bother with this?)
T🗸     __has_c_attribute
       __has_embed
       __has_include
T🗸     constants
T🗸       __STDC_IEC_60559_TYPES__
T🗸       __STDC_IEC_60559_BFP__
T🗸       __STDC_IEC_60559_DFP__
T🗸       __STDC_EMBED_NOT_FOUND__
T🗸       __STDC_EMBED_FOUND__
T🗸       __STDC_EMBED_EMPTY__
     enum
       explicit enumerated type
T🗸       parse
         check
T🗸         explicitly specified type is used as enumerated type
T🗸         type constraints
T🗸           must be integer type
T🗸           can't be bit-precise
T🗸           can't be enum
T🗸         qualifiers are discarded
T🗸         enum type is completed immediately after explicitly specified type is parsed
T🗸         forward declarations
T🗸           allowed when not used as type specifier (i.e. nothing else is declared)
T🗸           disallowed when used as type specifier
           multiple declarations of the same enum tag
             explicit enumerated type must be provided by either all declarations or none
               explicit enumerated type need not be (and can't be) provided when enum without definition is used as type specifier
T🗸           the enumerated types must all be compatible
T🗸         error if value would be implicitly truncated
T🗸           with initializer
T🗸           without initializer
T🗸     other type additions/changes
T🗸       implicit enumerated type
T🗸         order is (un)signed int, (un)signed long, (un)signed long long
T🗸       enumeration member type
T🗸         with implicit enumerated type
T🗸           within enum definition (each individual member)
T🗸             type is int if representable
T🗸             otherwise, if there's an initializer, type is that of the initializer
T🗸             otherwise, type is basic integer type with the same signedness as the previous member, whose rank is the minimum which isn't smaller than that of the previous member but which can represent the new implicit value
T🗸               error if no suitable type exists
T🗸           after enum is defined (all members collectively)
T🗸             type is int if all representable
T🗸             otherwise, type is the same as the enumerated type
T🗸         with explicit enumerated type
T🗸           type is the same as the enumerated type
T🗸             within enum definition
T🗸             after enum is defined
T🗸   storage-class specifiers in compound literals
T🗸     allowed
T🗸     disallowed for cast expressions
T🗸   treat empty parameter list as identical to (void)
   extensions
     preprocessor
       redefinition of macros
T🗸       user-defined
T🗸       pre-defined
         warns when new definition isn't identical to old
           doesn't warn when new definition is identical
X🗸     __DATE__ and __TIME__ use SOURCE_DATE_EPOCH if set
T🗸     __has_attribute
       linemarkers
     builtin identifiers
T🗸     can't be redeclared at top-level
       __builtin_offsetof
T🗸       parse
         check
           can't be used on bit-fields
T🗸         can be used on non-bit-fields
       __builtin_pp_embed
T🗸       parse
         check
         warnings and errors
           error when type isn't a character type
T🗸     __builtin_va_arg
T🗸       parse
X🗸       check
T🗸       warnings and errors
T🗸         when type name can't be made a pointer by postfixing it with *
T🗸           warn before c23
T🗸           no diagnostic since c23
T🗸         error when type name is an array type or function type
T🗸         error when type name is an incomplete type
T🗸         error when first argument isn't a va_list
T🗸     __builtin_va_copy
T🗸       parse
X🗸       check
T🗸       warnings and errors
T🗸         error when arguments aren't both va_lists
T🗸     __builtin_va_end
T🗸       parse
X🗸       check
T🗸       warnings and errors
T🗸         error when argument isn't a va_list
T🗸     __builtin_va_list
T🗸       parse
X🗸       check
       __builtin_va_start
 🗸       parse
 🗸         second argument must be identifier before c23
 🗸         second argument must be identifier or integer constant 0 after c23
X🗸       check
         warnings and errors
T🗸         error when function isn't variadic
T🗸         last named parameter has register storage-class specifier
T🗸           warn before c23
T🗸           no diagnostic since c23
           TODO: last named parameter is declared as array type or function type?
T🗸         last named parameter's type is incompatible with type after default argument promotion
T🗸           disallowed types
T🗸             integer type ranked lower than int
T🗸             float
T🗸           warn before c23
T🗸           no diagnostic since c23
           optional argument isn't the last named function parameter
             error before c23
             warn since c23
     __asm__ declarations
T🗸     parse
T🗸       disallowed on function definition
T🗸       disallowed for struct/union fields
T🗸       no other restrictions
T🗸       empty string unparses correctly
T🗸       trigraph-like character sequences are escaped when unparsed (prior to c23)
       check
T🗸       disallowed when no linkage
T🗸       allowed otherwise
T🗸       empty symbol disallowed
T🗸       no other restrictions imposed on symbol
T🗸       duplicate declarations must agree on symbol (if present)
         TODO: what's the deal with main?
T🗸   casting between object pointer and function pointer
T🗸     errors
T🗸      when POSIX_C isn't used
T🗸      no diagnostic otherwise
X🗸     does the Right Thing
     __attribute__
       locations
         declaration
           parse
           always applies only here, even when ambiguous with binding identifier
         binding identifier
T🗸       binding object
T🗸         parse
T🗸         must go after __asm__ if present
 🗸       struct/union/enum
 🗸         parse
T🗸           after struct/union/enum token
 🗸           after closing brace
 🗸             always applies only here, even when ambiguous with binding object
T🗸       enum field
T🗸       null statement
T🗸         parse
T🗸         only if not labelled
         label
T🗸         parse
T🗸         disallowed with case and default
T🗸         attribute always applies to label where ambiguous
           warn when labelled statement isn't an unattributed null statement since c23
       warn for unrecognized attributes
       supported attributes
         packed
T🗸         on struct
           on struct field
T🗸         on union
           on union field
         section
         aligned
         noreturn, _Noreturn
         deprecated
         fallthrough
         unused, maybe_unused
         nodiscard
         reproducible
         unsequenced
         always_inline
         noinline
         format
         format_arg
         visibility
         const
         pure
 🗸     attributes may also be used as c23 attributes with gnu:: prefix
T🗸     leading+trailing underscores permitted
T🗸   __typeof__
T🗸     equivalent to typeof, except allowed in all versions
T🗸   __typeof_unqual__
T🗸     equivalent to typeof_unqual, except allowed in all versions
T🗸   imaginary constants
T🗸     suffixes
T🗸       i
T🗸       j
T🗸     supported after c99
T🗸     not supported before c99
   additional warnings
     declaring a reserved identifier
       excluding potentially reserved (even prior to c23)
     defining a reserved identifier
       ...unless it's lexically equivalent to a keyword
       excluding potentially reserved (even prior to c23)
     SOURCE_DATE_EPOCH is invalid
     unused internally-linked objects and functions
T🗸   statement follows statement which manipulates control flow
T🗸     statement being followed
T🗸       break
T🗸       continue
T🗸       goto
T🗸       return
T🗸       potentially labelled
T🗸       compound whose last statement is any of these
T🗸       nothing else
T🗸     following statement
T🗸       warn if not a null statement
T🗸         some other statement
T🗸         declaration
T🗸           bindings or tag
T🗸           static assertion
T🗸       don't warn if null statement
T🗸         warn for following non-null statements which fit the requirements
T🗸       only warn if unlabelled
     unreachable code in switch statement
T🗸   switch statement has no cases