Cast/Paren precedence/assoc shift-reduce conflict #1039
Replies: 3 comments 8 replies
-
|
I haven't read and understood everything yet, but note that making something right-associative or left-associative when there's only one recurrence of There's only one recursive occurrence of Associativity only has effect when there are at least two recursive occurrences of the current rule |
Beta Was this translation helpful? Give feedback.
-
|
I'm starting to think this is something weird with precedence. |
Beta Was this translation helpful? Give feedback.
-
|
AFAIK, the conflict is that at this point parser don't know where to reduce for is not possible because parser needs to look ahead two symbols. Assuming we have a Bison-like reduce/reduce solution:
Found some "elegant" solution in C's |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I think I have found something weird with precedence.
Following the table C operator precedence I built the
exprproduction rule (and a few other things), but I'm running into a problem with thecast.I understand that there is a
shift-reduceconflict going on here because of the“(” Type . “)” Exprand“(” Expr . “)”states can shift to form a cast or reduce to form a paren, because Type and Expr can be Identifier, so the following case occurs:where
.marks the current position of the parser.What happens is that if I understand correctly, assigning higher precedence to the
castthan to theparenand making thecastright-associativeand theparenleft-associativeshould solve the conflict.The attached grammar should accomplish that, but the parser fails to generate due to the conflict. The line marked for deletion is the
castline, which when commented out causes everything to compile, and the one marked for addition is theparenline.use crate::token::{Token, TokenKind, LexicalError}; use crate::ast::AST; use lalrpop_util::ErrorRecovery; grammar<'err>(errors: &'err mut Vec<ErrorRecovery<usize, TokenKind, LexicalError>>); extern { type Location = usize; type Error = LexicalError; enum TokenKind { "error" => TokenKind::Error(_), // ======== // Keywords // ======== // Modifiers "const" => TokenKind::Const, "static" => TokenKind::Static, // Types "never" => TokenKind::Never, "void" => TokenKind::Void, "u8" => TokenKind::U8, "u16" => TokenKind::U16, "u32" => TokenKind::U32, "u64" => TokenKind::U64, "u128" => TokenKind::U128, "i8" => TokenKind::I8, "i16" => TokenKind::I16, "i32" => TokenKind::I32, "i64" => TokenKind::I64, "i128" => TokenKind::I128, "f32" => TokenKind::F32, "f64" => TokenKind::F64, // ========= // Operators // ========= // Assignment "=" => TokenKind::Assign, "~=" => TokenKind::TildeAssign, "+=" => TokenKind::PlusAssign, "-=" => TokenKind::MinusAssign, "*=" => TokenKind::StarAssign, "/=" => TokenKind::SlashAssign, "%=" => TokenKind::PercentAssign, "^=" => TokenKind::CaretAssign, "&=" => TokenKind::BitAndAssign, "|=" => TokenKind::BitOrAssign, "<<=" => TokenKind::ShlAssign, ">>=" => TokenKind::ShrAssign, "&&=" => TokenKind::AndAssign, "||=" => TokenKind::OrAssign, // Prefix "~" => TokenKind::Tilde, // Arithmetic "+" => TokenKind::Plus, "-" => TokenKind::Minus, "*" => TokenKind::Star, "/" => TokenKind::Slash, "%" => TokenKind::Percent, // Bitwise "&" => TokenKind::BitAnd, "^" => TokenKind::Caret, "|" => TokenKind::BitOr, "<<" => TokenKind::Shl, ">>" => TokenKind::Shr, // Logical "&&" => TokenKind::And, "||" => TokenKind::Or, // Comparison "==" => TokenKind::Eq, "!=" => TokenKind::Ne, "<" => TokenKind::Lt, "<=" => TokenKind::Le, ">" => TokenKind::Gt, ">=" => TokenKind::Ge, // Increment/Decrement "++" => TokenKind::Increment, "--" => TokenKind::Decrement, // ===== // Atoms // ===== // Identifiers "ident" => TokenKind::Identifier, // Integers "rawint" => TokenKind::RawInt, "decint" => TokenKind::DecInt, "binint" => TokenKind::BinInt, "octint" => TokenKind::OctInt, "hexint" => TokenKind::HexInt, // Floats "float" => TokenKind::Float, "intfloat" => TokenKind::IntFloat, "floatexp" => TokenKind::FloatExp, "intexp" => TokenKind::IntExp, // Strings "str" => TokenKind::String, "rstr" => TokenKind::RawString, // ======= // Symbols // ======= // Enclosures "(" => TokenKind::LParen, ")" => TokenKind::RParen, "{" => TokenKind::LBrace, "}" => TokenKind::RBrace, "[" => TokenKind::LBracket, "]" => TokenKind::RBracket, // Punctuation "." => TokenKind::Dot, "," => TokenKind::Comma, ":" => TokenKind::Colon, ";" => TokenKind::Semicolon, "!" => TokenKind::Bang, "?" => TokenKind::Question, "->" => TokenKind::Arrow, } } pub Source: AST = { TopLevel* => AST::Source(<>), } // ====== // Macros // ====== Comma<T>: Vec<T> = { <mut v:(<T> ",")*> <e:T?> => match e { None => v, Some(e) => { v.push(e); v } } }; // ========== // Statements // ========== TopLevel: AST = { FuncDecl, } FuncDecl: AST = { <ty:Type> <name:Token<"ident">> "(" <args:Comma<FuncDeclArg>> ")" <body:Expr> => AST::FunctionDeclaration(name, Box::new(ty), args, Box::new(body)), ! => { errors.push(<>); AST::Error }, } FuncDeclArg: (Token, AST) = { <ty:Type> <name:Token<"ident">> => (name, ty), } Type: AST = { Identifier, IntType, FloatType, } Block: AST = { "{" <Stmt*> "}" => AST::Block(<>), } Stmt: AST = { <Expr> ";", ! => { errors.push(<>); AST::Error }, } // =========== // Expressions // =========== FuncCall: AST = { <ident:Identifier> "(" <args:Comma<Expr>> ")" => AST::FunctionCall(Box::new(ident), args), } Expr: AST = { #[precedence(level="0")] Term, #[precedence(level="1")] #[assoc(side="left")] <lhs:Identifier> <op:Token<"++">> => AST::PostfixOp(Box::new(lhs), op), <lhs:Identifier> <op:Token<"--">> => AST::PostfixOp(Box::new(lhs), op), FuncCall, <expr:Expr> "[" <index:Expr> "]" => AST::Index(Box::new(expr), Box::new(index)), //member access #[precedence(level="2")] #[assoc(side="right")] <op:Token<"++">> <rhs:Expr> => AST::PrefixOp(op, Box::new(rhs)), <op:Token<"--">> <rhs:Expr> => AST::PrefixOp(op, Box::new(rhs)), <op:Token<"+">> <rhs:Expr> => AST::PrefixOp(op, Box::new(rhs)), <op:Token<"-">> <rhs:Expr> => AST::PrefixOp(op, Box::new(rhs)), <op:Token<"!">> <rhs:Expr> => AST::PrefixOp(op, Box::new(rhs)), <op:Token<"~">> <rhs:Expr> => AST::PrefixOp(op, Box::new(rhs)), - "(" <ty:Type> ")" <expr:Expr> => AST::Cast(Box::new(ty), Box::new(expr)), "*" <rhs:Expr> => AST::Deref(Box::new(rhs)), "&" <rhs:Expr> => AST::Ref(Box::new(rhs)), #[precedence(level="3")] #[assoc(side="left")] <lhs:Expr> <op:Token<"*">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"/">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"%">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="4")] #[assoc(side="left")] <lhs:Expr> <op:Token<"+">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"-">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="5")] #[assoc(side="left")] <lhs:Expr> <op:Token<"<<">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<">>">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="6")] #[assoc(side="left")] <lhs:Expr> <op:Token<"<">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"<=">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<">">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<">=">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="7")] #[assoc(side="left")] <lhs:Expr> <op:Token<"==">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"!=">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="8")] #[assoc(side="left")] <lhs:Expr> <op:Token<"&">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="9")] #[assoc(side="left")] <lhs:Expr> <op:Token<"^">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="10")] #[assoc(side="left")] <lhs:Expr> <op:Token<"|">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="11")] #[assoc(side="left")] <lhs:Expr> <op:Token<"&&">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="12")] #[assoc(side="left")] <lhs:Expr> <op:Token<"||">> <rhs:Expr> => AST::BinaryOp(Box::new(lhs), op, Box::new(rhs)), #[precedence(level="13")] #[assoc(side="right")] <cond:Expr> "?" <then:Expr> ":" <r#else:Expr> => AST::TernaryOp(Box::new(cond), Box::new(then), Box::new(r#else)), #[precedence(level="14")] #[assoc(side="right")] <lhs:Expr> <op:Token<"=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"+=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"-=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"*=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"/=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"%=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"&=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"^=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"|=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<"<<=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), <lhs:Expr> <op:Token<">>=">> <rhs:Expr> => AST::Assignment(Box::new(lhs), op, Box::new(rhs)), } Term: AST = { Atom, Block, + "(" <Expr> ")", } Atom: AST = { Identifier, Int, Float, String, } // ================ // Token definition // ================ Token<Kind>: Token = <start: @L> <kind: Kind> <end: @R> => Token::new(kind, start..end); Identifier: AST = { Token<"ident"> => AST::Identifier(<>), } Int: AST = { Token<"decint"> => AST::Integer(<>), Token<"binint"> => AST::Integer(<>), Token<"octint"> => AST::Integer(<>), Token<"hexint"> => AST::Integer(<>), } Float: AST = { Token<"float"> => AST::Float(<>), Token<"intfloat"> => AST::Float(<>), Token<"floatexp"> => AST::Float(<>), Token<"intexp"> => AST::Float(<>), } String: AST = { Token<"str"> => AST::String(<>), Token<"rstr"> => AST::String(<>), } IntType: AST = { Token<"u8"> => AST::IntType(<>), Token<"u16"> => AST::IntType(<>), Token<"u32"> => AST::IntType(<>), Token<"u64"> => AST::IntType(<>), Token<"u128"> => AST::IntType(<>), Token<"i8"> => AST::IntType(<>), Token<"i16"> => AST::IntType(<>), Token<"i32"> => AST::IntType(<>), Token<"i64"> => AST::IntType(<>), Token<"i128"> => AST::IntType(<>), } FloatType: AST = { Token<"f32"> => AST::FloatType(<>), Token<"f64"> => AST::FloatType(<>), }Beta Was this translation helpful? Give feedback.
All reactions