Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

c_parse.pl

The C parser (milestone 4a): it turns the tokens from c_lex into a syntax tree.

The only semantic state is the set of typedef names in scope, which the grammar needs to tell declarations from expressions. It is the assoc Env, from each name to t, threaded through the nonterminals. Types, constants and tags are resolved later, in c_types and c_lower.

Translation unit

A translation unit is a list of these items:

ItemIs
declLoc, Specs, Initsa declaration; Inits is a list of initDcl, Init, Init none, an expression or listEntries
fundefLoc, Specs, Dcl, Bodya function definition
static_assertLoc, Expr, MessageMessage the string's bytes, or [] without one

Entries are entryDesignators, Init terms, each designator fieldName or indexExpr.

Declarators

A declarator is dclName, Type, Asm. Name is someIdentifier, or none in abstract declarators; Asm is asmSymbol or none. Type is built around the atom base, which stands for the type the Specs give:

TypeMeans
basethe type of the specifiers
ptrT, Qualsa pointer to T, Quals a list of qualifiers
arrayT, SizeSize none or an expression
funcT, ParamsParams unspecified, or paramsPs, Variadic

In paramsPs, Variadic, Ps is a list of paramSpecs, Dcl and Variadic is fixed or variadic.

Specifiers

Specs is a list of:

SpecifierMeans
storageSS is typedef, extern, static, auto, register or thread_local
tspecKa type specifier keyword such as int or unsigned
qualQa qualifier: const, volatile, restrict or atomic
fspecFinline or noreturn
tnameNamea typedef name
suKind, Tag, Members, AttrsKind struct or union
enumTag, Enumeratorsan enum
attrTokensan attribute
alignastypeTypeName alignasexprE_Alignas
typeoftypeTypeName typeofexprEtypeof

A Tag is someIdentifier or none. Members is none, or a list of fieldSpecs, Dcl, Width and static_assert terms, Width none or an expression. Enumerators is none, or a list of enumeratorName, Value, Value an expression or none.

Statements

A statement is atLoc, S, with S one of:

SNotes
blockItemsItems are statements, decl and static_assert terms
exprE
nullthe empty statement
ifC, Then, ElseElse none without an else
whileC, S doS, C
forInit, C, Step, SInit none, exprE or a decl term; C and Step may be none
switchE, S caseE, S defaultS
break continue
returnEE may be none
gotoL labelL, S

Expressions

ExpressionMeans
idNan identifier
numV, Suffix−Radixan integer constant, as c_lex gives it
chrV floatCharscharacter and floating constants
strBytesa string literal
wstrElemType, Codesa wide string literal
binOp, A, Ba binary operator
assignOp, A, BOp is = for plain assignment
unOp, AOp neg, plus, bnot, not, deref, addr, preinc, predec, postinc or postdec
condC, A, BC ? A : B
gcondC, BGNU C's C ?: B
commaA, B
callF, Args indexA, I
memberA, F arrowA, FA.F and A->F
castTypeName, E
sizeof_typeTypeName sizeof_exprE alignofTypeName
complitTypeName, Inita compound literal
offsetofTypeName, PathPath a list of fieldF and indexE
va_argA, TypeName__builtin_va_arg
stmtexprBlocka GNU statement expression
genericControl, Associations_Generic, Associations a list of assocTypeName, E and assocdefault, E

A TypeName is typeSpecs, Type.

useslists
usesdcgs
usesassoc
c_parse+Tokens, -Itemsdet

Items is the translation unit of the c_lex Tokens, as described above. Throws nanofil_error/1 on a syntax error or unsupported construct.

116c_parseTokens, Items ←
list_to_assoc'__builtin_va_list'−t, Env
phraseunitEnv, Items, Tokens
c_constant_expression+Tokens, -Exprdet

Expr is the constant expression, a conditional expression, that the c_lex Tokens are, as #if reads them. No name is a typedef. Throws nanofil_error/1 on a syntax error.

125c_constant_expressionTokens, Expr ←
empty_assocEnv
phraseconstant_expressionEnv, Expr, Tokens
129constant_expressionEnv, Expr ⟶
conditionalEnv, Expr
→
[]
else
parse_error'expected the end of the expression'
unit+Env, -Items//

the external declarations up to the end of input. The cut commits to the first parse of each declaration.

135unit_, [] ⟶
136unitEnv0, Items ⟶
external_declEnv0, Env, Items, Items1
!
unitEnv, Items1
139unit_, _ ⟶
parse_error'expected a declaration'
141eos[], []

Tokens

145tokenT ⟶
tokT, _
146punctP ⟶
tokpP, _
147kwK ⟶
tokkwK, _

Lookahead: the next token, left in the input.

150peekT, tokT, Loc ⟶
tokT, Loc

The next token's location, or eof.

153locLoc, tokT, Loc ⟶
tokT, Loc
154loceof ⟶
expect+P//

the punctuator P, or a syntax error.

157expectP ⟶
158expectP ⟶
formatatomMsg, 'expected ~w', P
parse_error+Msg//

throw a syntax error at the next token.

161parse_errorMsg, tokT, Loc ⟶
tokT, Loc
!
token_textT, Text
formatatomM, '~w: ~w, at ~w', Loc, Msg, Text
thrownanofil_errorM
167parse_errorMsg ⟶
formatatomM, 'end of input: ~w', Msg
thrownanofil_errorM
token_text+T, -Text

Text shows the c_lex token T in a message.

token_text
173pPP
174kwKK
175idNN
176intV, SintV, S
177floatCsfloatCs
178chrVchrV
179strBsstrBs
180wstrT, VswstrT, Vs

External declarations

external_decl+Env0, -Env, -Items, ?Items1//

one external declaration; Items is Items1 with its item in front, or Items1 itself for a stray ';'. Env is Env0 with its typedef names.

187external_declEnv, Env, Items, Items ⟶
!
188external_declEnv0, Env, Items, Items1 ⟶
kwextension
!
external_declEnv0, Env, Items, Items1
191external_declEnv, Env, Item|Items, Items ⟶
locLoc
kwstatic_assert
!
static_assert_restEnv, Loc, Item
195external_decl_, _, _, _ ⟶
kwasm
!
parse_error'top-level asm is not supported'
198external_declEnv0, Env, Item|Items, Items ⟶
locLoc
decl_specsEnv0, Specs
!
→
Item = declLoc, Specs, []
Env = Env0
else
full_declaratorEnv0, Dcl, Attrs0
Dcl = dclsomeName, func_, Params, _
peekp'{'
→
typedefs_hiddenName, Env0, Env
params_envParams, Env, BodyEnv
blockBodyEnv, Body
appendSpecs, Attrs0, Specs1
Item = fundefLoc, Specs1, Dcl, Body
else
init_declaratorsEnv0, Dcl, Inits, Attrs0, Attrs
appendSpecs, Attrs, Specs1
Item = declLoc, Specs1, Inits
decl_envSpecs, Inits, Env0, Env
static_assert_rest+Env, +Loc, -Item//

a _Static_assert after its keyword.

223static_assert_restEnv, Loc, static_assertLoc, Expr, Msg ⟶
conditionalEnv, Expr
else
Msg = []
decl_env+Specs, +Inits, +Env0, -Env

Env is Env0 with the typedef names the declaration adds or (for ordinary names) hides.

232decl_envSpecs, Inits, Env0, Env ←
findallN, memberinitdclsomeN, _, _, _, Inits, Names
memberchkstoragetypedef, Specs
→
foldltypedef_declared, Names, Env0, Env
else
typedefs_hiddenNames, Env0, Env
typedef_declared+Name, +Env0, -Env

Env is Env0 with typedef Name.

240typedef_declaredName, Env0, Env ←
put_assocName, Env0, t, Env
typedefs_hidden+Names, +Env0, -Env

Env is Env0 where none of Names is a typedef name; typedef_hidden/3 is the same for one Name.

244typedefs_hiddenNames, Env0, Env ←
foldltypedef_hidden, Names, Env0, Env
246typedef_hiddenName, Env0, Env ←
del_assocName, Env0, _, Env1
→
Env = Env1
else
Env = Env0
params_env+Params, +Env0, -Env

Env is Env0 for a function body, where parameter names hide typedefs.

251params_envparamsParams, _, Env0, Env ←
findallN, memberparam_, dclsomeN, _, _, Params, Names
typedefs_hiddenNames, Env0, Env
254params_envunspecified, Env, Env
init_declarators+Env, +Dcl, -Inits, +Attrs0, -Attrs//

Inits are the init(Dcl, Init) of the declarator list whose first declarator Dcl is already read. The declarators' attributes are collected into Attrs and apply to the whole declaration.

260init_declaratorsEnv, Dcl, initDcl, Init|Inits, Attrs0, Attrs ⟶
→
initializerEnv, Init
else
Init = none
punct(',')
→
full_declaratorEnv, Dcl1, Attrs1
appendAttrs0, Attrs1, Attrs2
init_declaratorsEnv, Dcl1, Inits, Attrs2, Attrs
else
Inits = []
Attrs = Attrs0

Declaration specifiers

decl_specs+Env, -Specs//

nonempty declaration specifiers.

272decl_specsEnv, Specs ⟶
specifiersEnv, no, Specs
Specs ≢ []
specifiers+Env, +TypeSeen, -Specs//

once a type specifier is seen, an identifier is the declarator, not a typedef name.

278specifiersEnv, TypeSeen, Spec|Specs ⟶
specifierEnv, TypeSeen, Spec
!
spec_type_seenSpec, TypeSeen, TypeSeen1
specifiersEnv, TypeSeen1, Specs
282specifiers_, _, [] ⟶
[]
spec_type_seen+Spec, +TypeSeen0, -TypeSeen

TypeSeen is yes if Spec is a type specifier, else TypeSeen0.

spec_type_seen
286storage_TypeSeenTypeSeen
287tspec__yes
288qual_TypeSeenTypeSeen
289fspec_TypeSeenTypeSeen
290attr_TypeSeenTypeSeen
291alignas_TypeSeenTypeSeen
292su_, _, _, __yes
293enum_, __yes
294typeof__yes
295tname__yes
297specifier_, _, storageS ⟶
memberchkS, typedef, extern, static, auto, register, (thread_local)
!
301specifier_, _, tspecK ⟶
memberchkK, void, char, short, int, long, float, double, signed, unsigned, bool, complex, int128
!
306specifier_, _, _ ⟶
kwatomic
!
parse_error'_Atomic types are not supported'
310specifier_, _, qualQ ⟶
memberchkQ, const, (volatile), restrict, atomic
!
312specifier_, _, fspecF ⟶
memberchkF, inline, noreturn
!
314specifier_, _, attrA ⟶
315specifier_, _, attrc23 ⟶
316specifier_, _, attrextension ⟶
kwextension
!
317specifierEnv, _, alignasA ⟶
kwalignas
!
type_nameEnv, T
→
A = typeT
else
A = exprE
326specifierEnv, _, Spec ⟶
memberchkK, struct, union
!
struct_specEnv, K, Spec
329specifierEnv, _, Spec ⟶
kwenum
!
enum_specEnv, Spec
332specifierEnv, _, typeofA ⟶
kwtypeof
!
type_nameEnv, T
→
A = typeT
else
exprEnv, E
A = exprE
341specifierEnv, no, tnameN ⟶
get_assocN, Env, t
gnu_attr-Tokens//

__attribute__((...)), Tokens its balanced//1 contents.

347gnu_attrA ⟶
kwattribute
A = A0
balanced-Toks//

tokens up to the ')' matching an already-consumed '('; nested parentheses become group(Toks).

355balanced[] ⟶
!
356balancedT|Ts ⟶
!
T = groupInner
361balancedT|Ts ⟶
tokT, _
!
364balanced_ ⟶
parse_error'unbalanced parentheses'

c23_attr//: a standard attribute specifier. Do not confuse its opening '[[' with an array suffix. Arguments are balanced token sequences, not C expressions (unknown vendors may use their own argument language).

374c23_attrs ⟶
375c23_attrs ⟶
[]
377c23_attr_entries ⟶
else
[]
else
[]
390attribute_wordN ⟶
!
391attribute_wordN ⟶

Like their GNU spellings, layout-changing attributes cannot be ignored. Reject here also at positions (e.g. pointers) whose attributes we discard.

395c23_attribute_semanticsPrefix, Name ⟶
memberchkPrefix, gnu, '__gnu__'
memberchkName, packed, '__packed__', aligned, '__aligned__'
!
parse_error'alignment attributes are not supported'
399c23_attribute_semantics_, _ ⟶
[]
401attribute_argumentClose ⟶
punctClose
!
402attribute_argumentClose ⟶
406attribute_argumentClose ⟶
¬memberchkT, p')', p']', p'}'
!
410attribute_argument_ ⟶
parse_error'unbalanced attribute argument'
attribute_delimiter
412'('')'
413'['']'
414'{''}'

attrs//: GNU and C23 attributes, ignored.

417attrs ⟶
420attrs ⟶
421attrs ⟶
[]
423struct_specEnv, Kind, suKind, Tag, Members, Attrs ⟶
attr_listAttrs0
tokenidTag0
→
Tag = someTag0
else
Tag = none
→
membersEnv, Members
attr_listAttrs1
else
Tag = some_
Members = none
Attrs1 = []
appendAttrs0, Attrs1, Attrs
433attr_listA|As ⟶
436attr_listAs ⟶
437attr_list[] ⟶
[]
members+Env, -Members//

the member declarations of a struct or union up to its '}'.

441members_, [] ⟶
!
442membersEnv, Members ⟶
!
membersEnv, Members
445membersEnv, static_assertLoc, E, Msg|Members ⟶
locLoc
kwstatic_assert
!
else
Msg = []
membersEnv, Members
454membersEnv, Members ⟶
decl_specsEnv, Specs
!
→
Members = fieldSpecs, dclnone, base, none, none|Members1
else
field_declsEnv, Specs, Members, Members1
membersEnv, Members1
462members_, _ ⟶
parse_error'expected a member declaration'
field_decls+Env, +Specs, -Fields, ?Rest//

Fields-Rest is the difference list of fields one member declaration declares.

466field_declsEnv, Specs, fieldFieldSpecs, Dcl, Width|Fields, Rest ⟶
→
Dcl = dclnone, base, none
Attrs = []
conditionalEnv, Width
else
full_declaratorEnv, Dcl, Attrs
→
conditionalEnv, Width
else
Width = none
attr_listAttrs1
findallattrX, memberX, Attrs1, Attrs2
appendSpecs, Attrs, Attrs2, FieldSpecs
punct(',')
→
field_declsEnv, Specs, Fields, Rest
else
Fields = Rest
482enum_specEnv, enumTag, Enumerators ⟶
tokenidTag0
→
Tag = someTag0
else
Tag = none
→
enumeratorsEnv, Enumerators
else
Tag = some_
Enumerators = none
enumerators+Env, -Enumerators//

the enumerators up to the '}'.

492enumerators_, [] ⟶
!
493enumeratorsEnv, enumeratorName, Value|Es ⟶
tokenidName
!
→
Value = V0
else
Value = none
punct(',')
→
else
Es = []
498enumerators_, _ ⟶
parse_error'expected an enumerator'

Declarators

full_declarator+Env, -Dcl, -Attrs//

a declarator with its GNU suffix. Attributes after a declarator come back as attr(Tokens) specifiers in Attrs.

505full_declaratorEnv, dclsomeName, Type, Asm, Attrs ⟶
declaratorEnv, Name, Type, base
gnu_suffixAsm, Attrs
gnu_suffix-Asm, -Attrs//

attributes and an asm("symbol") label.

510gnu_suffixAsm, Attrs ⟶
attr_listAttrs0
kwasm
→
atom_codesSymbol, Bytes
Asm = asmSymbol
else
Asm = none
attr_listAttrs1
appendAttrs0, Attrs1, Attrs2
findallattrX, memberX, Attrs2, Attrs
declarator+Env, -Name, -Type, +Base//

a declarator of Name; Type is built around Base.

526declaratorEnv, Name, Type, Base ⟶
!
declaratorEnv, Name, Type, ptrBase, Quals
530declaratorEnv, Name, Type, Base ⟶
direct_declaratorEnv, Name, Type, Base
534direct_declaratorEnv, Name, Type, Base ⟶
tokenidName
!
suffixesEnv, Base, Type
537direct_declaratorEnv, Name, Type, Base ⟶
declaratorEnv, Name, Type, Mid
suffixesEnv, Base, Mid
543qualifiersQuals ⟶
memberchkQ, const, (volatile), restrict, atomic
!
Quals = Q|Quals1
547qualifiersQuals ⟶
550qualifiersQuals ⟶
551qualifiers[] ⟶
[]
abstract_declarator+Env, -Type, +Base//

like declarator//4 without a name. A '(' starts a nested declarator only before a '*'; otherwise it is a parameter list.

556abstract_declaratorEnv, Type, Base ⟶
!
abstract_declaratorEnv, Type, ptrBase, Quals
560abstract_declaratorEnv, Type, Base ⟶
!
abstract_declaratorEnv, Type, Mid
suffixesEnv, Base, Mid
566abstract_declaratorEnv, Type, Base ⟶
suffixesEnv, Base, Type
suffixes+Env, +Base, -Type//

array and function suffixes, applied inside out: Type is Base with them.

570suffixesEnv, Base, Type ⟶
!
suffixesEnv, Base, Type
573suffixesEnv, Base, Type ⟶
!
array_sizeEnv, Size
suffixesEnv, Base, Type1
Type = arrayType1, Size
579suffixesEnv, Base, Type ⟶
!
paramsEnv, Params
suffixesEnv, Base, Type1
Type = funcType1, Params
584suffixes_, Base, Base ⟶
[]
array_size+Env, -Size//

the size between '[' and ']', or none; the ']' stays in the input.

588array_size_, none, tokp']', Loc ⟶
tokp']', Loc
!
589array_size_, _ ⟶
peekp']'
!
parse_error'variable length arrays are not supported'
593array_sizeEnv, Size ⟶
kwstatic
!
array_sizeEnv, Size
596array_sizeEnv, Size ⟶
memberchkQ, const, (volatile), restrict
!
array_sizeEnv, Size
599array_sizeEnv, Size ⟶
assignmentEnv, Size
params+Env, -Params//

a parameter list after its '(', through ')'.

602params_, unspecified ⟶
!
603params_, params[], fixed ⟶
kwvoid
!
606paramsEnv, paramsParams, Variadic ⟶
param_listEnv, Params, Variadic
610param_list_, [], variadic ⟶
!
611param_listEnv, Param|Params, Variadic ⟶
paramEnv, Param
punct(',')
→
param_listEnv, Params, Variadic
else
Params = []
Variadic = fixed
618paramEnv, paramSpecs, dclName, Type, none ⟶
decl_specsEnv, Specs
!
declaratorEnv, Name0, Type, base
→
Name = someName0
else
abstract_declaratorEnv, Type, base
Name = none
626param_, _ ⟶
!
parse_error'K&R-style parameter lists are not supported'
629param_, _ ⟶
parse_error'expected a parameter declaration'
type_name+Env, -TypeName//

a type name, type(Specs, Type).

632type_nameEnv, typeSpecs, Type ⟶
decl_specsEnv, Specs
abstract_declaratorEnv, Type, base
starts_type+Env//

lookahead: the next token begins a type name. __extension__ can prefix either a type or an expression. Look past it without consuming it, rather than misreading glibc's ({...}) as a type.

639starts_typeEnv, tokkwextension, Loc ⟶
tokkwextension, Loc
!
642starts_typeEnv, tokX, Loc ⟶
tokX, Loc
646type_start_tokenkwK, _ ←
memberchkK, void, char, short, int, long, float, double, signed, unsigned, bool, complex, int128, struct, union, enum, const, (volatile), restrict, atomic, typeof, attribute, alignas, extension
651type_start_tokenidN, Env ←
get_assocN, Env, t

Initializers

initializer+Env, -Init//

an expression or a braced list(Entries).

657initializerEnv, listEntries ⟶
!
init_entriesEnv, Entries
660initializerEnv, E ⟶
init_entries+Env, -Entries//

the entries of a braced initializer after its '{', through '}'.

664init_entries_, [] ⟶
!
665init_entriesEnv, entryDesignators, Init|Entries ⟶
designatorsEnv, Ds0
Ds0 ≢ []
→
Designators = Ds0
else
Designators = []
initializerEnv, Init
punct(',')
→
init_entriesEnv, Entries
else
Entries = []
678designatorsEnv, D|Ds ⟶
!
681designators_, [] ⟶
[]
683designator_, fieldName ⟶
tokenidName
686designatorEnv, indexE ⟶
→
parse_error'GNU range designators are not supported'
else

Statements

696blockEnv, atLoc, blockItems ⟶
locLoc
block_itemsEnv, Items
block_items+Env, -Items//

the items of a block through its '}'.

702block_items_, [] ⟶
!
703block_itemsEnv0, Item|Items ⟶
block_itemEnv0, Env, Item
block_itemsEnv, Items
block_item+Env0, -Env, -Item//

a declaration or statement in a block; Env is Env0 with the declaration's typedef names.

709block_itemEnv, Env, Item ⟶
locLoc
kwstatic_assert
!
static_assert_restEnv, Loc, Item
713block_itemEnv0, Env, Item ⟶
!
local_declEnv0, Env, Item
716block_itemEnv, Env, Stmt ⟶
statementEnv, Stmt
starts_decl+Env//

lookahead only: the next tokens begin a declaration, not a label.

720starts_declEnv, S, S ←
phrasec23_attrs, S, Rest
¬phraselabel_start, Rest, _
phrasestarts_typeEnv, Rest, _
→
true
else
Rest = tokkwK, _|_
memberchkK, typedef, extern, static, auto, register, (thread_local), inline, noreturn

label_start//: lookahead: the next tokens are an identifier and ':'.

731label_start, A, B ⟶
A, B
A = tokid_, _
B = tokp:, _
local_decl+Env0, -Env, -Decl//

a declaration in a block or for.

736local_declEnv0, Env, declLoc, Specs1, Inits ⟶
locLoc
decl_specsEnv0, Specs
→
Inits = []
Attrs = []
else
full_declaratorEnv0, Dcl, Attrs0
init_declaratorsEnv0, Dcl, Inits, Attrs0, Attrs
appendSpecs, Attrs, Specs1
decl_envSpecs, Inits, Env0, Env
statement+Env, -Stmt//

a statement at(Loc, S). The cut commits to the first parse; if there is none, it is a syntax error.

751statementEnv, atLoc, S ⟶
755statement_, _ ⟶
parse_error'expected a statement'
757statement_bodyEnv, S ⟶
peekp'{'
!
blockEnv, at_, S
760statement_body_, null ⟶
!
761statement_bodyEnv, ifC, Then, Else ⟶
kwif
!
exprEnv, C
statementEnv, Then
kwelse
→
statementEnv, Else0
Else = Else0
else
Else = none
772statement_bodyEnv, whileC, S ⟶
kwwhile
!
exprEnv, C
statementEnv, S
778statement_bodyEnv, doS, C ⟶
kwdo
!
statementEnv, S
kwwhile
→
[]
else
parse_error'expected while'
exprEnv, C
786statement_bodyEnv, forInit, C, Step, S ⟶
kwfor
!
→
local_declEnv, Env1, Init
else
opt_exprEnv, E0
Env1 = Env
E0 ≡ none
→
Init = none
else
Init = exprE0
opt_exprEnv1, C
opt_exprEnv1, Step
statementEnv1, S
802statement_bodyEnv, switchE, S ⟶
kwswitch
!
exprEnv, E
statementEnv, S
808statement_bodyEnv, caseE, S ⟶
kwcase
!
→
parse_error'GNU case ranges are not supported'
else
statementEnv, S
816statement_bodyEnv, defaultS ⟶
kwdefault
!
statementEnv, S
820statement_body_, break ⟶
kwbreak
!
823statement_body_, continue ⟶
kwcontinue
!
826statement_bodyEnv, returnE ⟶
kwreturn
!
opt_exprEnv, E
830statement_body_, gotoLabel ⟶
kwgoto
!
tokenidLabel0
→
Label = Label0
else
parse_error'computed goto is not supported'
837statement_body_, _ ⟶
kwasm
!
parse_error'inline asm is not supported'
840statement_bodyEnv, labelLabel, S ⟶
!
tokidLabel, _, _
statementEnv, S
844statement_bodyEnv, exprE ⟶
exprEnv, E
opt_expr+Env, -E//

an expression, or none before ';' or ')', which stays in the input.

850opt_expr_, none, tokpP, Loc ⟶
tokpP, Loc
memberchkP, (;), ')'
!
852opt_exprEnv, E ⟶
exprEnv, E

Expressions

expr+Env, -E//

an expression, commas included.

857exprEnv, E ⟶
assignmentEnv, E0
punct(',')
→
exprEnv, E1
E = commaE0, E1
else
E = E0
865assignmentEnv, E ⟶
conditionalEnv, Lhs
→
assignmentEnv, Rhs
E = assignOp, Lhs, Rhs
else
E = Lhs
873assign_op=
874assign_opOp ←
compound_assignment+Spelling, -Opsemidet

Spelling is the compound assignment of the arithmetic or shift operator Op: its spelling followed by = (C11 6.5.16.2).

880compound_assignmentSpelling, Op ←
atom_concatP, =, Spelling
c_operatorP, _, Op, Kind
Kind = arith_
or
Kind ≡ shift
!
885conditionalEnv, E ⟶
binaryEnv, 1, C
→
→
E = gcondC, B
else
exprEnv, A
E = condC, A, B
else
E = C
binary+Env, +Level, -E//

binary operators of precedence Level and up; level 11 is a cast expression.

901binaryEnv, 11, E ⟶
!
castEnv, E
902binaryEnv, Level, E ⟶
Level1 is Level + 1
binaryEnv, Level1, Lhs
binary_restEnv, Level, Lhs, E
907binary_restEnv, Level, Lhs, E ⟶
c_operatorP, Level, Op, _
!
Level1 is Level + 1
binaryEnv, Level1, Rhs
binary_restEnv, Level, binOp, Lhs, Rhs, E
912binary_rest_, _, E, E ⟶
[]
c_operator?Spelling, ?Level, ?Op, ?Kindnondet

C's binary operators (C11 6.5.5-6.5.14): Spelling is Op, of precedence Level (1 binds loosest). Kind says what it does to its operands: arith(Domain) converts integer or arithmetic operands to their common type, shift promotes each, compare converts and compares, and logical tests each for truth.

c_operator
921'||'1lorlogical
922&&2landlogical
923('|')3orarithinteger
924^4xorarithinteger
925&5andarithinteger
926==6eqcompare
927'!='6necompare
928<7ltcompare
929>7gtcompare
930<=7lecompare
931>=7gecompare
932<<8shlshift
933>>8shrshift
934+9addaritharithmetic
935-9subaritharithmetic
936*10mularitharithmetic
937/10divaritharithmetic
938'%'10modarithinteger
cast+Env, -E//

a cast expression or compound literal, else a unary expression.

942castEnv, E ⟶
!
type_nameEnv, Type
→
init_entriesEnv, Entries
postfixEnv, complitType, listEntries, E
else
castEnv, E0
E = castType, E0
953castEnv, E ⟶
unaryEnv, E
955unaryEnv, unpreinc, E ⟶
!
unaryEnv, E
958unaryEnv, unpredec, E ⟶
!
unaryEnv, E
961unaryEnv, unOp, E ⟶
unopP, Op
!
castEnv, E
964unaryEnv, E ⟶
kwsizeof
!
→
type_nameEnv, Type
E = sizeof_typeType
else
unaryEnv, E0
E = sizeof_exprE0
973unaryEnv, alignofType ⟶
kwalignof
!
type_nameEnv, Type
978unaryEnv, E ⟶
kwextension
!
castEnv, E
981unary_, _ ⟶
!
parse_error'label addresses are not supported'
984unaryEnv, E ⟶
primaryEnv, P
postfixEnv, P, E
unop
988&addr
989*deref
990+plus
991-neg
992~bnot
993!not
postfix+Env, +A, -E//

E is A with the postfix operators that follow.

996postfixEnv, A, E ⟶
!
exprEnv, I
postfixEnv, indexA, I, E
1001postfixEnv, A, E ⟶
!
argsEnv, Args
postfixEnv, callA, Args, E
1005postfixEnv, A, E ⟶
!
postfixEnv, memberA, F, E
1009postfixEnv, A, E ⟶
punct(->)
!
postfixEnv, arrowA, F, E
1013postfixEnv, A, E ⟶
!
postfixEnv, unpostinc, A, E
1016postfixEnv, A, E ⟶
!
postfixEnv, unpostdec, A, E
1019postfix_, E, E ⟶
[]
1021field_nameF ⟶
!
1022field_name_ ⟶
parse_error'expected a member name'
args+Env, -Args//

call arguments after '(', through ')'.

1025args_, [] ⟶
!
1026argsEnv, A|As ⟶
punct(',')
→
argsEnv, As
else
As = []
primary+Env, -E//

a primary expression.

1035primaryEnv, offsetofType, Path ⟶
tokenid'__builtin_offsetof'
!
type_nameEnv, Type
expect(',')
offsetof_pathEnv, Steps
Path = fieldF|Steps
1044primaryEnv, va_argA, Type ⟶
tokenid'__builtin_va_arg'
!
expect(',')
type_nameEnv, Type
1048primary_, _ ⟶
memberchkN, '__builtin_types_compatible_p'
!
formatatomMsg, '~w is not supported', N
1054primary_, idN ⟶
!
1055primary_, numV, S ⟶
tokenintV, S
!
1056primary_, floatCs ⟶
tokenfloatCs
!
1057primary_, chrV ⟶
tokenchrV
!
1058primary_, S ⟶
peekstr_
or
peekwstr_, _
!
parts_stringParts, S
1062primaryEnv, stmtexprBlock ⟶
peekp'{'
!
blockEnv, Block
1067primaryEnv, genericControl, Associations ⟶
kwgeneric
!
assignmentEnv, Control
expect(',')
generic_associationsEnv, Associations
1074primaryEnv, E ⟶
!
exprEnv, E
1078primary_, _ ⟶
parse_error'expected an expression'
1080generic_associationsEnv, assocType, Expr|Rest ⟶
kwdefault
→
Type = default
else
type_nameEnv, Type
assignmentEnv, Expr
punct(',')
→
else
Rest = []
1090offsetof_pathEnv, fieldF|Steps ⟶
1094offsetof_pathEnv, indexE|Steps ⟶
!
exprEnv, E
offsetof_pathEnv, Steps
1099offsetof_path_, [] ⟶
[]
string_literals-Parts//

one or more adjacent string literals, each str(Bytes) or wstr(Type, Codes). They concatenate; if one is wide, all are, and a plain one's bytes become elements (exact for ASCII).

1104string_literalsS|Ss ⟶
!
else
Ss = []
1108string_literalstrB ⟶
tokenstrB
1109string_literalwstrT, Vs ⟶
tokenwstrT, Vs
string_bytes-Bytes//

adjacent plain string literals as one list of bytes.

1113string_bytesBs ⟶
tokenstrB0
!
→
appendB0, B1, Bs
else
Bs = B0
parts_string+Parts, -S

S is the string literal Parts concatenate.

1118parts_stringParts, S ←
findallT, memberwstrT, _, Parts, Types0
sortTypes0, Types
findallVs,
memberP, Parts
P = strVs
or
P = wstr_, Vs
,
Vss
appendVss, All
types_stringTypes, All, S
types_string+WideTypes, +Elements, -S

S is the literal of Elements: plain if no part is wide, else of the one wide type.

1129types_string[], All, strAll
1130types_stringT|Ts, All, S ←
wide_stringTs, T, All, S
1132wide_string[], T, All, wstrT, All
1133wide_string_|_, _, _, _ ←
thrownanofil_error'concatenating string literals of different wide types'