query</code> is an acceptable single word name for a question mark so I am going with that in the lexer.</p>
The new keywords are simple, enough, but it requires updating the keyword map.</p>
pub const</span> keywords</span> =</span> std.StaticStringMap(Tag).initComptime(.{</span></span>
.{ </span>"int"</span>, .type_int },</span></span>
.{ </span>"return"</span>, .keyword_return },</span></span>
.{ </span>"void"</span>, .keyword_void },</span></span>
</span>
// new lines :</span></span>
.{ </span>"if"</span>, .keyword_if },</span></span>
.{ </span>"else"</span>, .keyword_else },</span></span>
});</span></span></code></pre>
The two new tokens are trivial to add and require no new states in the lexer's state machine. So onto AST and parsing.</p>
AST</h2>
One new statement and one new expression today. Compound statements survive another day without being implemented.</p>
pub const</span> Stmt</span> =</span> union</span>(</span>enum</span>) {</span></span>
// snip --</span></span>
@"if":</span> struct</span> { cond:</span> *</span>Expr, then:</span> *</span>Stmt, @"else":</span> ?*</span>Stmt },</span></span>
};</span></span></code></pre>pub const</span> Expr</span> =</span> union</span>(</span>enum</span>) {</span></span>
// snip --</span></span>
ternary:</span> struct</span> { </span>*</span>Expr,</span> *</span>Expr,</span> *</span>Expr },</span></span>
};</span></span></code></pre>
And that is pretty much it for the AST. No revision of previous major design decisions.</p>
Parser</h2>
So how do you parse an expression like c ? a : b</code>?. The rule for it is that it is right associative (not unlike the assignment operator =</code>), but</em> the expression in the middle is treated as it is part of the expression itself or between two parenthesis. In other words, think of ? a :</code> as an binary operator between c</code> and b</code> all by itself, only for parsing purposes. So first update the binop_precedence</code> function</p>
pub fn</span> binop_precedence(self:</span> @This</span>()) </span>?</span>struct</span> { </span>u8</span>,</span> u8</span> } {</span></span>
return switch</span> (self) {</span></span>
.query</span> =></span> .{ </span>3</span>,</span> 0</span> },</span></span>
// the rest</span></span>
};</span></span>
}</span></span></code></pre>
Then update parse_expr</code> to deal with it. This is slightly trickier than usual. The middle expression should be parsed before the right hand side is. Therefore this silly dance, before parsing rhs</code>.</p>
const</span> then_ptr:</span> ?*</span>ast.Expr</span> =</span> if</span> (current.tag</span> ==</span> .query) t: {</span></span>
const</span> then</span> =</span> try</span> parse_expr(arena, tokens,</span> 0</span>);</span></span>
try</span> expect(.colon, tokens);</span></span>
</span>
break</span> :t</span> try</span> utils.create(ast.Expr, arena, then);</span></span>
} </span>else</span> null</span>;</span></span></code></pre>
Then, in the big switch board, this tiny line is added.</p>
.greater_equals</span> =></span> .{ .binop_ge</span> =</span> bin_op },</span></span>
.query</span> =></span> .{ .ternary</span> =</span> .{ lhs_ptr, then_ptr.</span>?</span>, rhs_ptr } },</span> // <-</span></span></code></pre>
And voila. Ternary expressions are parsed now. This creates a very tiny bit of waste creating the boiler-plate reducing bin_op</code> (which is shared between all the other expressions). But all is fair in the name of easier to write code.</p>
For parsing the if</code> statement, I hit upon a previous design decision. In adding declarations last chapter, I decided to forgo a separate parse_stmt</code> in exchange for a simpler, at the time, single parse_block_item</code>, and discriminating then between declarations and statements. Now, since the then</code> argument of the if</code> statement can only be a statement and not a declaration, this requires a divorce, and a separate parse_stmt</code>. Declarations are not ready to leave the house yet and therefore will remain in parse_block_item</code>. This is the trimmed parse_block_item</code>, followed by parse_stmt</code> building its own house.</p>
fn</span> parse_block_item(</span></span>
arena: std.mem.Allocator,</span></span>
tokens:</span> *</span>lexer.Tokenizer,</span></span>
) Error</span>!</span>ast.BlockItem {</span></span>
const</span> current</span> =</span> tokens.next() </span>orelse</span></span>
return</span> error</span>.NotEnoughJunk;</span></span>
</span>
switch</span> (current.tag) {</span></span>
.type_int</span> =></span> {</span></span>
const</span> name</span> =</span> try</span> expect(.identifier, tokens);</span></span>
const</span> new_token</span> =</span> tokens.next() </span>orelse</span></span>
return</span> error</span>.NotEnoughJunk;</span></span>
</span>
const</span> init:</span> ?*</span>ast.Expr</span> =</span> switch</span>