use rucc_ast::{BinaryOp, Designator, Expr, ExprId, ExprList, GenericAssoc, TypeNameId, UnaryOp};
use rucc_base::Symbol;
use rucc_lex::{Keyword, Punct, Token, TokenKind};
use crate::parser::Parser;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Infix {
Binary(BinaryOp),
Assign(Option<BinaryOp>),
Comma,
Cond,
}
fn infix(punct: Punct) -> Option<(Infix, u8, u8)> {
use BinaryOp as B;
let (what, lbp, rbp) = match punct {
Punct::Comma => (Infix::Comma, 1, 2),
Punct::Eq => (Infix::Assign(None), 4, 3),
Punct::StarEq => (Infix::Assign(Some(B::Mul)), 4, 3),
Punct::SlashEq => (Infix::Assign(Some(B::Div)), 4, 3),
Punct::PercentEq => (Infix::Assign(Some(B::Rem)), 4, 3),
Punct::PlusEq => (Infix::Assign(Some(B::Add)), 4, 3),
Punct::MinusEq => (Infix::Assign(Some(B::Sub)), 4, 3),
Punct::ShlEq => (Infix::Assign(Some(B::Shl)), 4, 3),
Punct::ShrEq => (Infix::Assign(Some(B::Shr)), 4, 3),
Punct::AmpEq => (Infix::Assign(Some(B::BitAnd)), 4, 3),
Punct::CaretEq => (Infix::Assign(Some(B::BitXor)), 4, 3),
Punct::PipeEq => (Infix::Assign(Some(B::BitOr)), 4, 3),
Punct::Question => (Infix::Cond, 6, 5),
Punct::PipePipe => (Infix::Binary(B::LogOr), 7, 8),
Punct::AmpAmp => (Infix::Binary(B::LogAnd), 9, 10),
Punct::Pipe => (Infix::Binary(B::BitOr), 11, 12),
Punct::Caret => (Infix::Binary(B::BitXor), 13, 14),
Punct::Amp => (Infix::Binary(B::BitAnd), 15, 16),
Punct::EqEq => (Infix::Binary(B::Eq), 17, 18),
Punct::Ne => (Infix::Binary(B::Ne), 17, 18),
Punct::Lt => (Infix::Binary(B::Lt), 19, 20),
Punct::Gt => (Infix::Binary(B::Gt), 19, 20),
Punct::Le => (Infix::Binary(B::Le), 19, 20),
Punct::Ge => (Infix::Binary(B::Ge), 19, 20),
Punct::Shl => (Infix::Binary(B::Shl), 21, 22),
Punct::Shr => (Infix::Binary(B::Shr), 21, 22),
Punct::Plus => (Infix::Binary(B::Add), 23, 24),
Punct::Minus => (Infix::Binary(B::Sub), 23, 24),
Punct::Star => (Infix::Binary(B::Mul), 25, 26),
Punct::Slash => (Infix::Binary(B::Div), 25, 26),
Punct::Percent => (Infix::Binary(B::Rem), 25, 26),
_ => return None,
};
Some((what, lbp, rbp))
}
const EXPRESSION: u8 = 1;
const ASSIGNMENT: u8 = 3;
const CONDITIONAL: u8 = 5;
impl Parser<'_> {
pub(crate) fn expr(&mut self) -> ExprId {
self.expr_bp(EXPRESSION)
}
pub(crate) fn assign_expr(&mut self) -> ExprId {
self.expr_bp(ASSIGNMENT)
}
pub(crate) fn const_expr(&mut self) -> ExprId {
self.expr_bp(CONDITIONAL)
}
fn infix_here(&self, min_bp: u8) -> Option<(Infix, u8)> {
let (what, lbp, rbp) = infix(self.cursor.current().punct()?)?;
if lbp < min_bp {
return None;
}
Some((what, rbp))
}
fn expr_bp(&mut self, min_bp: u8) -> ExprId {
let mut lhs = self.cast_expr();
while let Some((what, rbp)) = self.infix_here(min_bp) {
self.cursor.bump();
let start = self.ast.expr_span(lhs);
lhs = match what {
Infix::Cond => {
let then =
if self.cursor.at_punct(Punct::Colon) { None } else { Some(self.expr()) };
self.expect_punct(Punct::Colon);
let otherwise = self.expr_bp(rbp);
let span = start.to(self.ast.expr_span(otherwise));
self.add_expr(Expr::Cond { cond: lhs, then, otherwise }, span)
}
Infix::Comma => {
let rhs = self.expr_bp(rbp);
let span = start.to(self.ast.expr_span(rhs));
self.add_expr(Expr::Comma { lhs, rhs }, span)
}
Infix::Assign(op) => {
let rhs = self.expr_bp(rbp);
let span = start.to(self.ast.expr_span(rhs));
self.add_expr(Expr::Assign { op, lhs, rhs }, span)
}
Infix::Binary(op) => {
let rhs = self.expr_bp(rbp);
let span = start.to(self.ast.expr_span(rhs));
self.add_expr(Expr::Binary { op, lhs, rhs }, span)
}
};
}
lhs
}
fn cast_expr(&mut self) -> ExprId {
if !self.at_parenthesised_type() {
return self.unary();
}
let start = self.cursor.span();
if !self.enter() {
self.cursor.bump();
return self.poison_expr(start);
}
self.cursor.bump();
let ty = self.type_name();
self.expect_punct(Punct::RParen);
self.leave();
if self.cursor.at_punct(Punct::LBrace) {
let init = self.braced_init();
let span = self.span_from(start);
let literal = self.add_expr(Expr::CompoundLiteral { ty, init }, span);
return self.postfix_ops(literal);
}
let operand = self.cast_expr();
let span = start.to(self.ast.expr_span(operand));
self.add_expr(Expr::Cast { ty, operand }, span)
}
pub(crate) fn at_parenthesised_type(&self) -> bool {
self.cursor.at_punct(Punct::LParen) && self.starts_type_name(self.cursor.peek(1))
}
fn unary(&mut self) -> ExprId {
let start = self.cursor.span();
let token = self.cursor.current();
if let Some(punct) = token.punct() {
let prefix = match punct {
Punct::PlusPlus => Some((UnaryOp::PreInc, true)),
Punct::MinusMinus => Some((UnaryOp::PreDec, true)),
Punct::Amp => Some((UnaryOp::AddrOf, false)),
Punct::Star => Some((UnaryOp::Deref, false)),
Punct::Plus => Some((UnaryOp::Plus, false)),
Punct::Minus => Some((UnaryOp::Minus, false)),
Punct::Tilde => Some((UnaryOp::BitNot, false)),
Punct::Bang => Some((UnaryOp::Not, false)),
_ => None,
};
if let Some((op, unary_operand)) = prefix {
self.cursor.bump();
let operand = if unary_operand { self.unary() } else { self.cast_expr() };
let span = start.to(self.ast.expr_span(operand));
return self.add_expr(Expr::Unary { op, operand }, span);
}
if punct == Punct::AmpAmp {
self.cursor.bump();
let name = self.expect_ident();
let span = self.span_from(start);
return match name {
Some((name, _)) => self.add_expr(Expr::LabelAddr(name), span),
None => self.poison_expr(span),
};
}
}
if let Some(word) = token.keyword() {
match word {
Keyword::Sizeof => return self.sizeof_expr(),
Keyword::Alignof | Keyword::GnuAlignof => return self.alignof_expr(),
Keyword::Extension => {
self.cursor.bump();
let operand = self.cast_expr();
let span = start.to(self.ast.expr_span(operand));
return self.add_expr(Expr::Extension(operand), span);
}
Keyword::Real | Keyword::Imag => {
self.cursor.bump();
let op = if word == Keyword::Real { UnaryOp::Real } else { UnaryOp::Imag };
let operand = self.cast_expr();
let span = start.to(self.ast.expr_span(operand));
return self.add_expr(Expr::Unary { op, operand }, span);
}
_ => {}
}
}
let base = self.primary();
self.postfix_ops(base)
}
fn sizeof_expr(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if let Some(ty) = self.parenthesised_type_operand() {
if self.cursor.at_punct(Punct::LBrace) {
let init = self.braced_init();
let span = self.span_from(start);
let literal = self.add_expr(Expr::CompoundLiteral { ty, init }, span);
let operand = self.postfix_ops(literal);
let span = self.span_from(start);
return self.add_expr(Expr::SizeofExpr(operand), span);
}
let span = self.span_from(start);
return self.add_expr(Expr::SizeofType(ty), span);
}
let operand = self.unary();
let span = start.to(self.ast.expr_span(operand));
self.add_expr(Expr::SizeofExpr(operand), span)
}
fn alignof_expr(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if let Some(ty) = self.parenthesised_type_operand() {
let span = self.span_from(start);
return self.add_expr(Expr::AlignofType(ty), span);
}
let operand = self.unary();
let span = start.to(self.ast.expr_span(operand));
self.add_expr(Expr::AlignofExpr(operand), span)
}
fn parenthesised_type_operand(&mut self) -> Option<TypeNameId> {
if !self.at_parenthesised_type() {
return None;
}
self.cursor.bump();
let ty = self.type_name();
self.expect_punct(Punct::RParen);
Some(ty)
}
fn postfix_ops(&mut self, mut base: ExprId) -> ExprId {
loop {
let start = self.ast.expr_span(base);
let Some(punct) = self.cursor.current().punct() else { break };
match punct {
Punct::LBracket => {
if !self.enter() {
self.cursor.bump();
break;
}
self.cursor.bump();
let index = self.expr();
self.expect_punct(Punct::RBracket);
self.leave();
let span = self.span_from(start);
base = self.add_expr(Expr::Index { base, index }, span);
}
Punct::LParen => {
if !self.enter() {
self.cursor.bump();
break;
}
self.cursor.bump();
let args = self.call_arguments();
self.expect_punct(Punct::RParen);
self.leave();
let span = self.span_from(start);
base = self.add_expr(Expr::Call { callee: base, args }, span);
}
Punct::Dot | Punct::Arrow => {
let arrow = punct == Punct::Arrow;
self.cursor.bump();
let span_before = start;
match self.expect_ident() {
Some((name, _)) => {
let span = self.span_from(span_before);
base = self.add_expr(Expr::Member { base, name, arrow }, span);
}
None => {
let span = self.span_from(span_before);
base = self.poison_expr(span);
}
}
}
Punct::PlusPlus | Punct::MinusMinus => {
let op =
if punct == Punct::PlusPlus { UnaryOp::PostInc } else { UnaryOp::PostDec };
self.cursor.bump();
let span = self.span_from(start);
base = self.add_expr(Expr::Unary { op, operand: base }, span);
}
_ => break,
}
}
base
}
fn call_arguments(&mut self) -> ExprList {
let mut args = Vec::new();
if !self.cursor.at_punct(Punct::RParen) {
loop {
let before = self.cursor.index();
args.push(self.assign_expr());
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
if self.cursor.index() == before {
break;
}
}
}
self.ast.add_expr_list(&args)
}
fn primary(&mut self) -> ExprId {
let token = self.cursor.current();
let start = token.span;
match token.kind {
TokenKind::Ident => {
self.cursor.bump();
self.add_expr(Expr::Name(Symbol::from_raw(token.value)), start)
}
TokenKind::Int | TokenKind::Float | TokenKind::Char | TokenKind::Str => {
self.cursor.bump();
let expr = self.constant(token);
self.add_expr(expr, start)
}
TokenKind::Keyword(word) => self.primary_keyword(word),
TokenKind::Punct(Punct::LParen) => self.parenthesised(),
_ => {
let found = self.describe(token);
self.error("E0403", format!("expected an expression, found {found}"), start);
self.poison_expr(start)
}
}
}
fn constant(&mut self, token: Token) -> Expr {
let index = token.value as usize;
match token.kind {
TokenKind::Int => Expr::Int(self.ast.add_int(self.tokens.ints[index])),
TokenKind::Float => Expr::Float(self.ast.add_float(self.tokens.floats[index])),
TokenKind::Char => Expr::Char(self.ast.add_char(self.tokens.chars[index])),
TokenKind::Str => {
let literal = self.tokens.strings[index].clone();
Expr::Str(self.ast.add_string(literal))
}
_ => Expr::Error,
}
}
fn primary_keyword(&mut self, word: Keyword) -> ExprId {
let start = self.cursor.span();
match word {
Keyword::True => {
self.cursor.bump();
self.add_expr(Expr::Bool(true), start)
}
Keyword::False => {
self.cursor.bump();
self.add_expr(Expr::Bool(false), start)
}
Keyword::Nullptr => {
self.cursor.bump();
self.add_expr(Expr::Nullptr, start)
}
Keyword::Generic => self.generic_selection(),
Keyword::BuiltinOffsetof => self.builtin_offsetof(),
Keyword::BuiltinChooseExpr => self.builtin_choose_expr(),
Keyword::BuiltinTypesCompatibleP => self.builtin_types_compatible(),
Keyword::BuiltinVaArg => self.builtin_va_arg(),
_ => {
let found = self.describe(self.cursor.current());
self.error("E0403", format!("expected an expression, found {found}"), start);
self.poison_expr(start)
}
}
}
fn parenthesised(&mut self) -> ExprId {
let start = self.cursor.span();
if !self.enter() {
self.cursor.bump();
return self.poison_expr(start);
}
self.cursor.bump();
let result = if self.cursor.at_punct(Punct::LBrace) {
let body = self.compound_stmt();
let span = self.span_from(start);
self.add_expr(Expr::StmtExpr(body), span)
} else {
self.expr()
};
self.expect_punct(Punct::RParen);
self.leave();
result
}
fn generic_selection(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return self.poison_expr(start);
}
let control = self.assign_expr();
let mut assocs = Vec::new();
while self.cursor.eat_punct(Punct::Comma) {
let before = self.cursor.index();
let ty = if self.cursor.eat_keyword(Keyword::Default) {
None
} else {
Some(self.type_name())
};
self.expect_punct(Punct::Colon);
let value = self.assign_expr();
assocs.push(GenericAssoc { ty, value });
if self.cursor.index() == before {
break;
}
}
self.expect_punct(Punct::RParen);
let assocs = self.ast.add_generic_list(&assocs);
let span = self.span_from(start);
self.add_expr(Expr::Generic { control, assocs }, span)
}
fn builtin_offsetof(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return self.poison_expr(start);
}
let ty = self.type_name();
self.expect_punct(Punct::Comma);
let mut path = Vec::new();
if let Some((name, _)) = self.expect_ident() {
path.push(Designator::Field(name));
}
loop {
if self.cursor.eat_punct(Punct::Dot) {
match self.expect_ident() {
Some((name, _)) => path.push(Designator::Field(name)),
None => break,
}
} else if self.cursor.at_punct(Punct::LBracket) {
self.cursor.bump();
let index = self.expr();
self.expect_punct(Punct::RBracket);
path.push(Designator::Index(index));
} else {
break;
}
}
self.expect_punct(Punct::RParen);
let path = self.ast.add_designator_list(&path);
let span = self.span_from(start);
self.add_expr(Expr::Offsetof { ty, path }, span)
}
fn builtin_choose_expr(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return self.poison_expr(start);
}
let cond = self.assign_expr();
self.expect_punct(Punct::Comma);
let then = self.assign_expr();
self.expect_punct(Punct::Comma);
let otherwise = self.assign_expr();
self.expect_punct(Punct::RParen);
let span = self.span_from(start);
self.add_expr(Expr::ChooseExpr { cond, then, otherwise }, span)
}
fn builtin_types_compatible(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return self.poison_expr(start);
}
let a = self.type_name();
self.expect_punct(Punct::Comma);
let b = self.type_name();
self.expect_punct(Punct::RParen);
let span = self.span_from(start);
self.add_expr(Expr::TypesCompatible { a, b }, span)
}
fn builtin_va_arg(&mut self) -> ExprId {
let start = self.cursor.span();
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return self.poison_expr(start);
}
let list = self.assign_expr();
self.expect_punct(Punct::Comma);
let ty = self.type_name();
self.expect_punct(Punct::RParen);
let span = self.span_from(start);
self.add_expr(Expr::VaArg { list, ty }, span)
}
}