~sebsite/hare-c

C lexer/parser/type-checker for Hare
all: update per breaking changes in dependencies
Use done type and for-each accumulator loops
hacc: don't try to link when error(s) occurred

refs

main
browse  log 

clone

read-only
https://git.sr.ht/~sebsite/hare-c
read/write
git@git.sr.ht:~sebsite/hare-c

You can also use your local clone with git send-email.

#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 Hare module
  • cmd/cdecl - WIP-ish converter from C gibberish to readable English, and vice-versa. depends on madeline
  • cmd/hacc - C compiler
  • cmd/hareconv - converter between C headers and Hare modules

#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

A checkmark means the thing is implemented; a T means the thing is properly tested. This checklist isn't exhaustive; I'm updating it as I go.

   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
T🗸             incomplete types
T🗸               final member may be incomplete array if other members are present
T🗸               everything else disallowed
T🗸               struct type is still complete; size is as though incomplete array weren't present
T🗸               but struct type can't be used in other structs or in arrays
T🗸                 including if it's (recursively) a member of a union
 🗸           union
 🗸             declarations
 🗸               named
T🗸                 parse
 🗸                 check
 🗸               anonymous
T🗸                 parse
 🗸                 check
 🗸               definitions
T🗸                 parse
 🗸                 check
 🗸               forward
T🗸                 parse
 🗸                 check
T🗸             incomplete types disallowed
             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🗸           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
           enum
T🗸           declarations
T🗸             named
T🗸             anonymous
T🗸             definitions
T🗸             forward disallowed
 🗸           fields inserted into scope
T🗸             parse
 🗸             no linkage
T🗸             not lvalue
T🗸               can't take address
 🗸           tag inserted into scope immediately after definition ends
 🗸           prior to c23, implicit underlying type is int
             since c23, implicit underlying type follows order int, unsigned int, long, unsigned long, long long, unsigned long long
             duplicate fields disallowed
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 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 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
 🗸           arrays converted to (qualified) pointers
 🗸             still checked as arrays
 🗸           functions converted to function pointers
T🗸           may not declare more than one (non-tag) identifier
T🗸           may not be initialized
T🗸           without storage specifier
T🗸             defaults to implicit auto
 🗸           register storage specifier
T🗸             parse
T🗸             declares with register storage
 🗸             ignored for non-definitions
 🗸               may be different in compatible types
 🗸             applies even when other declarations' parameters have different/absent storage specifiers
T🗸           all other specifiers disallowed
 🗸           empty parameter list designates unspecified number of parameters
T🗸           treated as unqualified when taking composite type
T🗸         return type requirements
T🗸           may not return array
T🗸           may not return function
 🗸         warn when function returning complete type doesn't have return statement
T🗸           except for main function in hosted environment
 🗸           all other functions
T🗸         storage 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
         array
T🗸         without qualifiers
 🗸         qualifiers apply to element type
 🗸           don't apply to array itself before c23
           ...except as inner-most declarator of function parameter
           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
T🗸           complete
T🗸           object
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
 🗸     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
T🗸     literals
T🗸       numbers
T🗸         pre-processing numbers
T🗸         int literals
T🗸           bases
T🗸             decimal
T🗸             hex
T🗸             octal
T🗸           suffixes
T🗸             u
T🗸             l
T🗸           parse value
T🗸         float literals
T🗸           decimal
T🗸           exponent
T🗸           f suffix
T🗸           parse value
T🗸       char literals
T🗸         plain
T🗸         L prefix
T🗸       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)
       sizeof
T🗸       parse
 🗸       operand isn't evaluated if not variably modified
 🗸       operand is evaluated if variably modified
         operand must not be bit-field
T🗸       operand must be complete object
T🗸     array indexing
T🗸       parse
       struct/union field accessing
T🗸       forms (parse)
T🗸         a.b
T🗸         a->b
T🗸       check
         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
T🗸           neither & nor * is evaluated
T🗸           constraints still apply
T🗸         everything else disallowed
T🗸     binary
T🗸       parse
T🗸     assignment
T🗸       parse
T🗸     parenthesized
T🗸     ternary
T🗸     operator precedence
       casting
T🗸       explicit
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🗸         everything else disallowed (except object <-> function; see extensions)
         implicit conversion
           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
T🗸           any -> void
 🗸           va_list -> va_list
             struct/union -> same struct/union
T🗸             tagged
               untagged
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
 🗸       everything else allowed (except for additional constraints for specific kind of constant expression)
       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
 🗸     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
T🗸         character constant
T🗸         translation-time sizeof
T🗸         alignof
T🗸         __builtin_offsetof
           cast
             additional expressions allowed as immediate operand
 🗸             float constant
 🗸             named constant with arithmetic type
               constexpr compound literal with arithmetic type
 🗸         everything else disallowed, unless not evaluated (sizeof/alignof)
 🗸           including pre-defined constants like true and false
       constant expression in initializer
 🗸       named constant
         arithmetic constant expression
 🗸         top-most expression may be anything not generally disallowed
 🗸         result must be arithmetic type
           limitations for operands
 🗸           integer constant
 🗸           float constant
 🗸           named constant
             constexpr compound literal
 🗸           character constant
 🗸           translation-time sizeof
 🗸           alignof
 🗸           __builtin_offsetof
 🗸           cast from arithmetic constant expression
 🗸       null pointer constant
 🗸         integer constant expression which evaluates to zero
T🗸         doesn't otherwise affect type conversion rules
 🗸       address constant
 🗸         unary &
 🗸           operand is lvalue designating object with static storage duration
 🗸           operand is function designator
 🗸           everything else disallowed
 🗸         cast
 🗸           from integer constant to pointer type
 🗸           from pointer type to pointer type
 🗸           all other casts prohibited, unless not evaluated
 🗸         implicit pointer conversion
 🗸           from lvalue designating array object with static storage duration
 🗸             including string literals and compound literals
 🗸           from function designator
 🗸         array indexing
 🗸         arrow expression (->)
T🗸         unary *
 🗸         everything else disallowed
 🗸         besides taking address, values of objects may not be used in evaluated sub-expressions
 🗸       address constant of complete object plus/minus integer constant expression
 🗸       everything else disallowed
 🗸     nothing else is a constant expression
       integer promotion
         unsigned -> unsigned
         unsigned -> signed
         signed -> unsigned
         signed -> signed
         char's signedness is taken into account
 🗸     additional errors
T🗸       don't error when not evaluated
T🗸         including because of short-circuiting
 🗸       division by zero
T🗸         integer division
T🗸         integer modulus division
 🗸         floating point 0.0/0.0
 🗸           division by zero ok when dividend isn't 0.0
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
     statements
       goto
T🗸       parse
T🗸       jump to any label within function
T🗸       disallow undefined labels
         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
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
 🗸       value is promoted
T🗸       no VMTs not in scope of switch statement may be in scope of any cases
 🗸     break
T🗸       parse
T🗸       allowed in loop
T🗸       allowed in switch statement
T🗸       disallowed everywhere else
 🗸       causes jump to outside whatever it applies to
T🗸         within check
 🗸         for-loop afterthought isn't evaluated
 🗸     continue
T🗸       parse
T🗸       allowed in loop
T🗸       disallowed everywhere else
 🗸       causes jump to end of loop it applies to
T🗸         within check
 🗸         so for-loop afterthought is evaluated, then condition
 🗸     return
T🗸       parse
T🗸       type of return value must be convertible to function return type
 🗸       warn when returning non-void from void function
 🗸       warn when used in noreturn function
T🗸     expression statements
T🗸       parse
T🗸       implicitly convert to void
 🗸     bindings
 🗸       TODO write more shit here (stuff handled in fromabinding_block)
T🗸       enters scope before initializer is checked
     initializers
T🗸     as initializer of declaration
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
T🗸       compound literal without cast expression
T🗸         parse
T🗸         check
T🗸     in cast expression (compound literal)
T🗸       parse
T🗸       check
T🗸     designators
T🗸       parse
T🗸         none
T🗸         array
T🗸         struct
T🗸       check
T🗸         none
T🗸         array
T🗸         struct
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🗸     must be constant expression for objects with non-automatic storage duration
T🗸     may be any expression otherwise
T🗸     multiple designators per object element may be given
T🗸     designators may be combined
       duplicate designators
 🗸       final object overwrites all previous objects
         warn
 🗸     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)
 🗸       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
 🗸     freestanding environment
 🗸       not required to be declared
 🗸       no requirements or restrictions imposed when declared
       hosted environment
         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
   pre-processor
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
 🗸     pre-defined macros
T🗸        __STDC__
T🗸        __STDC_HOSTED__
T🗸        __FILE__
T🗸        __LINE__
 🗸        __DATE__
 🗸        __TIME__
       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-pre-processing tokens
 🗸           constructed token won't be used for pre-processing (i.e. no # or ##)
 🗸     # has higher precedence than ##
T🗸     object-like
T🗸     function-like
T🗸       works when followed by left paren
T🗸       doesn't work when not followed by left paren
T🗸       parameters
T🗸         expand non-recursively
T🗸           expands macros
T🗸           everything else
T🗸         don't expand recursively
T🗸           from parameters
T🗸           from macros
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
T🗸   #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)
T🗸   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
 🗸   #error
T🗸     errors out
 🗸     error message uses all tokens
 🗸     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
T🗸   trigraphs
     k&r-style functions
 🗸     empty parameter list
       non-empty parameter list without types
 🗸     k&r-style parameter declarations
     implicit int
T🗸 c95
T🗸   digraphs
T🗸   __STDC_VERSION__
   c99
 🗸   pragma
T🗸     #pragma
T🗸     _Pragma
 🗸     STDC
 🗸       accept standardized
 🗸         FP_CONTRACT
 🗸         FENV_ACCESS
 🗸         CX_LIMITED_RANGE
T🗸       reject unstandardized
T🗸     implementation-defined
T🗸       accepted; ignored
T🗸   VLAs
T🗸     parse
T🗸     can't be initialized
     hex float literals
T🗸     prefix / base
       parse value
T🗸     exponent
     universal character names
T🗸     in char/string literals
T🗸       parse
       in identifiers
         parse
         c99: conform to annex D (different from c11 annex D)
         c11, c17: conform to annex D (different from c99 annex D)
         c23: follow same rules as regular unicode codepoints in identifiers
         when malformed, split into separate tokens
 🗸     disallowed: (<0xa0 && !='$' && !='@' && !='`') || (>=0xd800 && <0xe000)
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
 🗸     static array qualifier
 🗸       allowed in function parameter
 🗸       disallowed everywhere else
T🗸   restrict
T🗸     parse
T🗸   variadic macros
T🗸     use of ... in definition
T🗸     __VA_ARGS__
T🗸       allowed in variadic macro definition
T🗸       disallowed everywhere else
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
   c11
     specifiers
       _Thread_local
T🗸       parse
T🗸       may not appear alongside auto or register
T🗸       may not appear in block scope when no storage specifiers are present
T🗸       may not be used on function declaration
         must appear on all declarations of an object
       _Noreturn
T🗸       parse
T🗸       may appear more than once
         applies to function declaration
         information persists in check
       _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, function, or atomic
         as qualifier
 🗸         parse
           other qualifiers apply to atomic type, not to type being made atomic
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
     _Alignof
T🗸     parse
 🗸     operand isn't evaluated, even if variably modified
       operand must not be typedef or register
       operand must be complete object
       other operands allowed
         including bit-field
       can't be used in function declaration
       can't be used in bit-field declaration
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🗸     char literals
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
     unicode
T🗸     identifiers (without universal character names)
       universal character name 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
 🗸       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
             implicitly qualified as const
             must be object (not function)
             must not be atomic
               also applies to struct/union members
             must not be volatile or restrict qualified
           initializer
             must be provided
 🗸           must be constant expression
             value must be exactly representable in type
             pointer initializer must be null pointer
             integer initializer must be integer constant expression
             arithmetic initializer must be arithmetic constant expression
             real float initializer must have integer or real float type
             imaginary float initializer must have imaginary float type
             no additional restrictions for other kinds of initializers
             decimal float initializer must have decimal float type
     pre-processor
       #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, clang::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
         <this> is lexed as a system header string literal
       #warning
         actually warns
 🗸       warning message uses all tokens
 🗸       macros aren't expanded
       __VA_OPT__
         allowed in variadic macro definition
T🗸       disallowed everywhere else
 🗸     #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
     type inferencing with auto
       parse
       check
         infers type
         initializer must be provided
         must have object type (not function)
         must have exactly one declarator
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
         [[noreturn]], [[_Noreturn]]
           disallow argument
           applicable to
             function
             nothing else
         [[deprecated]]
           may have optional argument
           applicable to
             struct/union declaration
             typedef name
             object
             struct/union member
             function
             enum
             enum member
             nothing else
T🗸         __has_c_attribute => 202311L
           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
T🗸         __has_c_attribute => 202311L
         [[maybe_unused]]
           disallow argument
           applicable to
             struct/union declaration
             typedef name
             object
             struct/union member
             function
             enum
             enum member
             label
             nothing else
T🗸         __has_c_attribute => 202311L
         [[nodiscard]]
           may have optional argument
           applicable to
             function declaration
             struct/union definition
             enum definition
             nothing else
T🗸         __has_c_attribute => 202311L
           warn when value discarded
         [[reproducible]]
           disallow argument
           applicable to
             function declarator
             type specifier with function type
T🗸         __has_c_attribute => 202311L
         [[unsequenced]]
           disallow argument
           applicable to
             function declarator
             type specifier with function type
T🗸         __has_c_attribute => 202311L
       warn on non-standard
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
 🗸     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🗸     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
 🗸     constants
 🗸       __STDC_IEC_60559_TYPES__
 🗸       __STDC_IEC_60559_BFP__
 🗸       __STDC_IEC_60559_DFP__
 🗸       __STDC_EMBED_NOT_FOUND__
 🗸       __STDC_EMBED_FOUND__
 🗸       __STDC_EMBED_EMPTY__
     explicit enum underlying type
T🗸   storage specifiers in cast expression
T🗸     allowed for compound literals
T🗸     disallowed for other casts
T🗸   treat empty parameter list as identical to (void)
     array is qualified equivalently to element type
   extensions
     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
     defining keywords as macros
T🗸     allowed
       warns
     pre-declared 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_va_arg
T🗸       parse
 🗸       check
 🗸     __builtin_va_copy
T🗸       parse
 🗸       check
 🗸     __builtin_va_end
T🗸       parse
 🗸       check
 🗸     __builtin_va_list
T🗸       parse
 🗸       check
       __builtin_va_start
         parse
 🗸         require exactly two arguments before c23
           require at least one argument after c23
 🗸       check
         warnings and errors
           error when function isn't variadic
           warn when more than one optional argument is supplied (c23)
           optional argument isn't an identifier
             error before c23
             warn 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
         restrictions on symbol
T🗸         empty string disallowed
           TODO: other restrictions (dependent on qbe and target assembler)
T🗸         everything else allowed
 🗸       duplicate declarations must agree on symbol (if present)
         TODO: what's the deal with main?
 🗸   casting between object pointer and function pointer
 🗸     TODO: might want to error out if not POSIX
 🗸     warns
 🗸      when none of _POSIX_SOURCE, _POSIX_C_SOURCE, or _XOPEN_SOURCE are defined
 🗸      doesn't warn otherwise
T🗸     does the Right Thing
 🗸   __DATE__ and __TIME__ use SOURCE_DATE_EPOCH if set
     __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
         empty statement
           parse
 🗸         only if not labelled
 🗸       label
T🗸         parse
 🗸         disallowed with case and default
 🗸         no restrictions on labelled statement prior to c23
T🗸         labelled statement must be unattributed empty statement since c23
       warn for unrecognized attributes
       supported attributes
         packed
T🗸         on struct
           on struct field
         section
         aligned
 🗸     attributes may also be used as c23 attributes with gnu:: or clang:: prefix
T🗸     leading+trailing underscores permitted
T🗸   __typeof__
T🗸     equivalent to typeof, except allowed in all versions
   additional warnings
     declaring a reserved identifier
       before c23: including potentially reserved
       after c23: excluding potentially reserved
     defining a reserved identifier
       before c23: including potentially reserved
       after c23: excluding potentially reserved
     SOURCE_DATE_EPOCH is invalid
     unused internally-linked objects and functions