diff --git a/src/data/error.rs b/src/data/error.rs index 89894721..b9480ed0 100644 --- a/src/data/error.rs +++ b/src/data/error.rs @@ -510,6 +510,10 @@ pub enum CppError { /// '##' missing arguments #[error("'##' cannot appear at {} of macro expansion", if *(.0) { "start" } else { "end"})] HashHashMissingParameter(bool), + + /// The result of '##' is not a valid token + #[error("pasting formed '{0}{1}', an invalid preprocessing token")] + HashHashInvalid(Token, Token), } /// Lex errors are non-exhaustive and may have new variants added at any time diff --git a/src/data/lex.rs b/src/data/lex.rs index 3680846d..e609f626 100644 --- a/src/data/lex.rs +++ b/src/data/lex.rs @@ -299,6 +299,54 @@ impl Locatable { impl Token { pub const EQUAL: Token = Token::Assignment(AssignmentToken::Equal); + + pub fn concat(x: &Token, y: &Token) -> Option { + use Token::*; + use AssignmentToken::*; + use ComparisonToken::*; + let tok = match (x, y) { + (Whitespace(wx), Whitespace(wy)) => Whitespace(format!("{}{}", wx, wy)), + (Whitespace(_), tok) => tok.clone(), + (tok, Whitespace(_)) => tok.clone(), + + (Plus, Assignment(Equal)) => Assignment(AddEqual), + (Minus, Assignment(Equal)) => Assignment(SubEqual), + (Star, Assignment(Equal)) => Assignment(MulEqual), + (Divide, Assignment(Equal)) => Assignment(DivEqual), + (Mod, Assignment(Equal)) => Assignment(ModEqual), + (ShiftLeft, Assignment(Equal)) => Assignment(ShlEqual), + (ShiftRight, Assignment(Equal)) => Assignment(ShrEqual), + (Ampersand, Assignment(Equal)) => Assignment(AndEqual), + (BitwiseOr, Assignment(Equal)) => Assignment(OrEqual), + (Xor, Assignment(Equal)) => Assignment(XorEqual), + + (Assignment(Equal), Assignment(Equal)) => Comparison(EqualEqual), + (LogicalNot, Assignment(Equal)) => Comparison(NotEqual), + (Comparison(Less), Assignment(Equal)) => Comparison(LessEqual), + (Comparison(Greater), Assignment(Equal)) => Comparison(GreaterEqual), + + (Ampersand, Ampersand) => LogicalAnd, + (BitwiseOr, BitwiseOr) => LogicalOr, + + (Comparison(Less), Comparison(Less)) => ShiftLeft, + (Comparison(Greater), Comparison(Greater)) => ShiftRight, + + (Minus, Comparison(Greater)) => StructDeref, + (Hash, Hash) => HashHash, + + (Keyword(kx), Keyword(ky)) => Id(InternedStr::get_or_intern(format!("{}{}", kx, ky))), + (Keyword(kx), Id(idy)) => Id(InternedStr::get_or_intern(format!("{}{}", kx, idy))), + (Id(idx), Keyword(ky)) => Id(InternedStr::get_or_intern(format!("{}{}", idx, ky))), + (Id(idx), Id(idy)) => Id(InternedStr::get_or_intern(format!("{}{}", idx, idy))), + + // Chars and Strings cannot be concatenated with token pasting + // TODO implement Literals + + // TODO what about Keyword::UserTypedef as differentiated from Id? + _ => return None, + }; + Some(tok) + } } impl AssignmentToken { diff --git a/src/lex/replace.rs b/src/lex/replace.rs index 3ccb72ec..436ec69e 100644 --- a/src/lex/replace.rs +++ b/src/lex/replace.rs @@ -183,7 +183,17 @@ pub fn replace( continue; } let pending_hashhash = pending_hashhash.take().unwrap(); // We just checked that it's some - let concat_token = concat(pending_hashhash, succeeding_tok.clone(), &location); + let concat_token = Token::concat(&pending_hashhash, succeeding_tok) + .map(|tok| location.with(tok)) + .ok_or_else(|| { + location.with( + CppError::HashHashInvalid( + pending_hashhash.clone(), + succeeding_tok.clone(), + ) + .into(), + ) + }); replacements.push(concat_token); // TODO don't bypass pending continue; } @@ -480,10 +490,6 @@ fn stringify(args: Vec) -> Token { ))])) } -fn concat(x: Token, b: Token, location: &Location) -> CompileResult> { - todo!(); -} - fn wrap_error(location: &Location, err: CppError) -> Vec> { vec![Err(location.with(err.into()))] }