use super::{is_word, is_word_start, Lexer};
use crate::{GlslVersion, Span, SpanEncoding, Spanned};
#[derive(Debug, Clone, PartialEq)]
pub enum TokenStream {
Empty,
Custom {
kw: Spanned<String>,
content: Option<Spanned<String>>,
},
Invalid {
content: Spanned<String>,
},
Version {
kw: Span,
tokens: Vec<Spanned<VersionToken>>,
},
Extension {
kw: Span,
tokens: Vec<Spanned<ExtensionToken>>,
},
Line {
kw: Span,
tokens: Vec<Spanned<LineToken>>,
},
Define {
kw: Span,
ident_tokens: Vec<Spanned<DefineToken>>,
body_tokens: super::TokenStream,
},
Undef {
kw: Span,
tokens: Vec<Spanned<UndefToken>>,
},
IfDef {
kw: Span,
tokens: Vec<Spanned<ConditionToken>>,
},
IfNotDef {
kw: Span,
tokens: Vec<Spanned<ConditionToken>>,
},
If {
kw: Span,
tokens: Vec<Spanned<ConditionToken>>,
},
ElseIf {
kw: Span,
tokens: Vec<Spanned<ConditionToken>>,
},
Else {
kw: Span,
tokens: Vec<Spanned<ConditionToken>>,
},
EndIf {
kw: Span,
tokens: Vec<Spanned<ConditionToken>>,
},
Error {
kw: Span,
message: Option<Spanned<String>>,
},
Pragma {
kw: Span,
options: Option<Spanned<String>>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum VersionToken {
Num(usize),
Word(String),
InvalidNum(String),
Invalid(char),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExtensionToken {
Word(String),
Colon,
Invalid(char),
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineToken {
Num(usize),
Ident(String),
InvalidNum(String),
Invalid(char),
}
#[derive(Debug, Clone, PartialEq)]
pub enum DefineToken {
Ident(String),
Invalid(char),
LParen,
RParen,
Comma,
}
#[derive(Debug, Clone, PartialEq)]
pub enum UndefToken {
Ident(String),
Invalid(char),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConditionToken {
Num(usize),
Ident(String),
LineComment(String),
BlockComment {
str: String,
contains_eof: bool,
},
InvalidNum(String),
Invalid(char),
Defined,
Add,
Sub,
Mul,
Div,
Rem,
And,
Or,
Xor,
LShift,
RShift,
Flip,
EqEq,
NotEq,
Not,
Gt,
Lt,
Ge,
Le,
AndAnd,
OrOr,
LParen,
RParen,
Comma,
}
impl ConditionToken {
pub fn non_semantic_colour(&self) -> crate::syntax::SyntaxType {
use crate::syntax::SyntaxType;
match self {
ConditionToken::Num(_) => SyntaxType::Number,
ConditionToken::Ident(_) => SyntaxType::Ident,
ConditionToken::LineComment(_)
| ConditionToken::BlockComment { .. } => SyntaxType::Comment,
ConditionToken::InvalidNum(_) => SyntaxType::Invalid,
ConditionToken::Invalid(_) => SyntaxType::Invalid,
ConditionToken::Defined => SyntaxType::Keyword,
ConditionToken::Add
| ConditionToken::Sub
| ConditionToken::Mul
| ConditionToken::Div
| ConditionToken::Rem
| ConditionToken::And
| ConditionToken::Or
| ConditionToken::Xor
| ConditionToken::LShift
| ConditionToken::RShift
| ConditionToken::Flip
| ConditionToken::EqEq
| ConditionToken::NotEq
| ConditionToken::Not
| ConditionToken::Gt
| ConditionToken::Lt
| ConditionToken::Ge
| ConditionToken::Le
| ConditionToken::AndAnd
| ConditionToken::OrOr => SyntaxType::Operator,
ConditionToken::LParen => SyntaxType::Punctuation,
ConditionToken::RParen => SyntaxType::Punctuation,
ConditionToken::Comma => SyntaxType::Punctuation,
}
}
pub fn to_normal_token(
(token, span): Spanned<Self>,
) -> Spanned<super::Token> {
(
match token {
ConditionToken::Num(n) => super::Token::Num {
type_: super::NumType::Dec,
num: n.to_string(),
suffix: None,
},
ConditionToken::Ident(s) => super::Token::Ident(s),
ConditionToken::LineComment(s) => super::Token::LineComment(s),
ConditionToken::BlockComment { str, contains_eof } => {
super::Token::BlockComment { str, contains_eof }
}
ConditionToken::InvalidNum(s) => {
let mut num = String::new();
let mut chars = s.chars();
for char in chars.next() {
if char.is_ascii_digit() {
num.push(char);
} else {
break;
}
}
super::Token::Num {
type_: super::NumType::Dec,
num,
suffix: Some(chars.collect()),
}
}
ConditionToken::Invalid(c) => super::Token::Invalid(c),
ConditionToken::Defined => {
super::Token::Ident("defined".into())
}
ConditionToken::Add => super::Token::Op(super::OpTy::Add),
ConditionToken::Sub => super::Token::Op(super::OpTy::Sub),
ConditionToken::Mul => super::Token::Op(super::OpTy::Mul),
ConditionToken::Div => super::Token::Op(super::OpTy::Div),
ConditionToken::Rem => super::Token::Op(super::OpTy::Rem),
ConditionToken::And => super::Token::Op(super::OpTy::And),
ConditionToken::Or => super::Token::Op(super::OpTy::Or),
ConditionToken::Xor => super::Token::Op(super::OpTy::Xor),
ConditionToken::LShift => super::Token::Op(super::OpTy::LShift),
ConditionToken::RShift => super::Token::Op(super::OpTy::RShift),
ConditionToken::Flip => super::Token::Op(super::OpTy::Flip),
ConditionToken::EqEq => super::Token::Op(super::OpTy::EqEq),
ConditionToken::NotEq => super::Token::Op(super::OpTy::NotEq),
ConditionToken::Not => super::Token::Op(super::OpTy::Not),
ConditionToken::Gt => super::Token::Op(super::OpTy::Gt),
ConditionToken::Lt => super::Token::Op(super::OpTy::Lt),
ConditionToken::Ge => super::Token::Op(super::OpTy::Ge),
ConditionToken::Le => super::Token::Op(super::OpTy::Le),
ConditionToken::AndAnd => super::Token::Op(super::OpTy::AndAnd),
ConditionToken::OrOr => super::Token::Op(super::OpTy::OrOr),
ConditionToken::LParen => super::Token::LParen,
ConditionToken::RParen => super::Token::RParen,
ConditionToken::Comma => super::Token::Comma,
},
span,
)
}
}
pub(super) fn construct_empty<C: super::Char>(
lexer: &mut Lexer<C>,
directive_kw: String,
directive_kw_span: Span,
) -> TokenStream {
match directive_kw.as_ref() {
"version" => TokenStream::Version {
kw: directive_kw_span,
tokens: vec![],
},
"extension" => TokenStream::Extension {
kw: directive_kw_span,
tokens: vec![],
},
"line" => TokenStream::Line {
kw: directive_kw_span,
tokens: vec![],
},
"define" => TokenStream::Define {
kw: directive_kw_span,
ident_tokens: vec![],
body_tokens: vec![],
},
"undef" => TokenStream::Undef {
kw: directive_kw_span,
tokens: vec![],
},
"ifdef" => {
lexer.metadata.contains_conditional_directives = true;
TokenStream::IfDef {
kw: directive_kw_span,
tokens: vec![],
}
}
"ifndef" => {
lexer.metadata.contains_conditional_directives = true;
TokenStream::IfNotDef {
kw: directive_kw_span,
tokens: vec![],
}
}
"if" => {
lexer.metadata.contains_conditional_directives = true;
TokenStream::If {
kw: directive_kw_span,
tokens: vec![],
}
}
"elif" => {
lexer.metadata.contains_conditional_directives = true;
TokenStream::ElseIf {
kw: directive_kw_span,
tokens: vec![],
}
}
"else" => {
lexer.metadata.contains_conditional_directives = true;
TokenStream::Else {
kw: directive_kw_span,
tokens: vec![],
}
}
"endif" => {
lexer.metadata.contains_conditional_directives = true;
TokenStream::EndIf {
kw: directive_kw_span,
tokens: vec![],
}
}
"error" => TokenStream::Error {
kw: directive_kw_span,
message: None,
},
"pragma" => TokenStream::Pragma {
kw: directive_kw_span,
options: None,
},
_ => TokenStream::Custom {
kw: (directive_kw, directive_kw_span),
content: None,
},
}
}
pub(super) fn parse_version<C: super::Char>(
lexer: &mut Lexer<C>,
directive_kw_span: Span,
is_first_non_comment_token: bool,
) -> (TokenStream, Option<GlslVersion>) {
let mut tokens = Vec::new();
let mut buffer = String::new();
let mut first_number = true;
let mut version = None;
while !lexer.is_done() {
let buffer_start = lexer.position();
let mut current = match lexer.peek() {
Some(c) => c,
None => break,
};
if current == '\r' || current == '\n' {
break;
}
if is_word_start(¤t) {
buffer.push(current);
lexer.advance();
'word: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
tokens.push((
VersionToken::Word(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else {
tokens.push((
VersionToken::Word(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
}
} else if current.is_ascii_digit() {
buffer.push(current);
lexer.advance();
let mut invalid_num = false;
'number: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
if invalid_num {
tokens.push((
VersionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else {
match usize::from_str_radix(&buffer, 10) {
Ok(num) => {
if is_first_non_comment_token
&& first_number
{
version = GlslVersion::parse(num);
first_number = false;
}
tokens.push((
VersionToken::Num(num),
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
}
Err(_) => tokens.push((
VersionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
)),
}
}
break 'number;
}
};
if current.is_ascii_digit() {
buffer.push(current);
lexer.advance();
} else if current.is_ascii_alphabetic() {
invalid_num = true;
buffer.push(current);
lexer.advance();
} else {
if invalid_num {
tokens.push((
VersionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else {
match usize::from_str_radix(&buffer, 10) {
Ok(num) => {
if is_first_non_comment_token && first_number {
version = GlslVersion::parse(num);
first_number = false;
}
tokens.push((
VersionToken::Num(num),
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
}
Err(_) => tokens.push((
VersionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
)),
}
}
break 'number;
}
}
} else if current.is_whitespace() {
lexer.advance();
} else {
lexer.advance();
tokens.push((
VersionToken::Invalid(current),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
}
(
TokenStream::Version {
kw: directive_kw_span,
tokens,
},
version,
)
}
pub(super) fn parse_extension<C: super::Char>(
lexer: &mut Lexer<C>,
directive_kw_span: Span,
) -> TokenStream {
let mut tokens = Vec::new();
let mut buffer = String::new();
while !lexer.is_done() {
let buffer_start = lexer.position();
let mut current = match lexer.peek() {
Some(c) => c,
None => break,
};
if current == '\r' || current == '\n' {
break;
}
if is_word_start(¤t) {
buffer.push(current);
lexer.advance();
'word: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
tokens.push((
ExtensionToken::Word(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else {
tokens.push((
ExtensionToken::Word(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
}
} else if current == ':' {
lexer.advance();
tokens.push((
ExtensionToken::Colon,
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else if current.is_whitespace() {
lexer.advance();
} else {
lexer.advance();
tokens.push((
ExtensionToken::Invalid(current),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
}
TokenStream::Extension {
kw: directive_kw_span,
tokens,
}
}
pub(super) fn parse_line<C: super::Char>(
lexer: &mut Lexer<C>,
directive_kw_span: Span,
) -> TokenStream {
let mut tokens = Vec::new();
let mut buffer = String::new();
while !lexer.is_done() {
let buffer_start = lexer.position();
let mut current = match lexer.peek() {
Some(c) => c,
None => break,
};
if current == '\r' || current == '\n' {
break;
}
if is_word_start(¤t) {
buffer.push(current);
lexer.advance();
'word: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
tokens.push((
LineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else {
tokens.push((
LineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
}
} else if current.is_ascii_digit() {
buffer.push(current);
lexer.advance();
let mut invalid_num = false;
'number: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
if invalid_num {
tokens.push((
LineToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else {
match usize::from_str_radix(&buffer, 10) {
Ok(num) => {
tokens.push((
LineToken::Num(num),
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
}
Err(_) => tokens.push((
LineToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
)),
}
}
break 'number;
}
};
if current.is_ascii_digit() {
buffer.push(current);
lexer.advance();
} else if current.is_ascii_alphabetic() {
invalid_num = true;
buffer.push(current);
lexer.advance();
} else {
if invalid_num {
tokens.push((
LineToken::InvalidNum(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else {
match usize::from_str_radix(&buffer, 10) {
Ok(num) => {
tokens.push((
LineToken::Num(num),
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
}
Err(_) => tokens.push((
LineToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
)),
}
}
break 'number;
}
}
} else if current.is_whitespace() {
lexer.advance();
} else {
lexer.advance();
tokens.push((
LineToken::Invalid(current),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
}
TokenStream::Line {
kw: directive_kw_span,
tokens,
}
}
pub(super) fn parse_define<C: super::Char>(
lexer: &mut Lexer<C>,
) -> Vec<Spanned<DefineToken>> {
let mut tokens = Vec::new();
let mut current;
loop {
current = match lexer.peek() {
Some(c) => c,
None => return vec![],
};
if current == '\r' || current == '\n' {
return vec![];
}
if current.is_ascii_whitespace() {
lexer.advance();
continue;
} else {
break;
}
}
if !is_word_start(¤t) {
return vec![];
}
let mut buffer = String::new();
let buffer_start = lexer.position();
buffer.push(current);
lexer.advance();
loop {
current = match lexer.peek() {
Some(c) => c,
None => {
return vec![(
DefineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
)];
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else if current == '(' {
tokens.push((
DefineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
let pos = lexer.position();
lexer.advance();
tokens.push((
DefineToken::LParen,
Span {
start: pos,
end: lexer.position(),
},
));
break;
} else {
return vec![(
DefineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
)];
}
}
loop {
let token_start = lexer.position();
current = match lexer.peek() {
Some(c) => c,
None => break,
};
if current == '\r' || current == '\n' {
break;
}
if is_word_start(¤t) {
buffer.push(current);
lexer.advance();
'word: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
tokens.push((
DefineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: token_start,
end: lexer.position(),
},
));
break 'word;
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else {
tokens.push((
DefineToken::Ident(std::mem::take(&mut buffer)),
Span {
start: token_start,
end: lexer.position(),
},
));
break 'word;
}
}
} else if current == ',' {
lexer.advance();
tokens.push((
DefineToken::Comma,
Span {
start: token_start,
end: lexer.position(),
},
));
} else if current == ')' {
lexer.advance();
tokens.push((
DefineToken::RParen,
Span {
start: token_start,
end: lexer.position(),
},
));
break;
} else if current.is_whitespace() {
lexer.advance();
} else {
lexer.advance();
tokens.push((
DefineToken::Invalid(current),
Span {
start: token_start,
end: lexer.position(),
},
));
}
}
tokens
}
pub(super) fn parse_undef<C: super::Char>(
lexer: &mut Lexer<C>,
directive_kw_span: Span,
) -> TokenStream {
let mut tokens = Vec::new();
let mut buffer = String::new();
while !lexer.is_done() {
let buffer_start = lexer.position();
let mut current = match lexer.peek() {
Some(c) => c,
None => break,
};
if current == '\r' || current == '\n' {
break;
}
if is_word_start(¤t) {
buffer.push(current);
lexer.advance();
'word: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
tokens.push((
UndefToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else {
tokens.push((
UndefToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'word;
}
}
} else if current.is_whitespace() {
lexer.advance();
} else {
lexer.advance();
tokens.push((
UndefToken::Invalid(current),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
}
TokenStream::Undef {
kw: directive_kw_span,
tokens,
}
}
pub(super) fn parse_condition<C: super::Char>(
lexer: &mut Lexer<C>,
directive_kw: &str,
directive_kw_span: Span,
) -> TokenStream {
let tokens = parse_condition_tokens(lexer);
match directive_kw {
"ifdef" => TokenStream::IfDef {
kw: directive_kw_span,
tokens,
},
"ifndef" => TokenStream::IfNotDef {
kw: directive_kw_span,
tokens,
},
"if" => TokenStream::If {
kw: directive_kw_span,
tokens,
},
"elif" => TokenStream::ElseIf {
kw: directive_kw_span,
tokens,
},
"else" => TokenStream::Else {
kw: directive_kw_span,
tokens,
},
"endif" => TokenStream::EndIf {
kw: directive_kw_span,
tokens,
},
_ => unreachable!("Only one of the above `&str` values should be passed into this function!"),
}
}
pub(crate) fn parse_condition_tokens<C: super::Char>(
lexer: &mut Lexer<C>,
) -> Vec<Spanned<ConditionToken>> {
let mut tokens = Vec::new();
let mut buffer = String::new();
while !lexer.is_done() {
let buffer_start = lexer.position();
let mut current = match lexer.peek() {
Some(c) => c,
None => break,
};
if current == '\r' || current == '\n' {
break;
}
if is_word_start(¤t) {
buffer.push(current);
lexer.advance();
'word: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
if &buffer == "defined" {
tokens.push((
ConditionToken::Defined,
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
} else {
tokens.push((
ConditionToken::Ident(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
break 'word;
}
};
if is_word(¤t) {
buffer.push(current);
lexer.advance();
} else {
if &buffer == "defined" {
tokens.push((
ConditionToken::Defined,
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
} else {
tokens.push((
ConditionToken::Ident(std::mem::take(&mut buffer)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
break 'word;
}
}
} else if current.is_ascii_digit() {
buffer.push(current);
lexer.advance();
let mut invalid_num = false;
'number: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
if invalid_num {
tokens.push((
ConditionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else {
match usize::from_str_radix(&buffer, 10) {
Ok(num) => {
tokens.push((
ConditionToken::Num(num),
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
}
Err(_) => tokens.push((
ConditionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
)),
}
}
break 'number;
}
};
if current.is_ascii_digit() {
buffer.push(current);
lexer.advance();
} else if current.is_ascii_alphabetic() {
invalid_num = true;
buffer.push(current);
lexer.advance();
} else {
if invalid_num {
tokens.push((
ConditionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
} else {
match usize::from_str_radix(&buffer, 10) {
Ok(num) => {
tokens.push((
ConditionToken::Num(num),
Span {
start: buffer_start,
end: lexer.position(),
},
));
buffer.clear();
}
Err(_) => tokens.push((
ConditionToken::InvalidNum(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
)),
}
}
break 'number;
}
}
} else if is_conditional_punctuation_start(¤t) {
if lexer.take_pat("//") {
'line_comment: loop {
current = match lexer.peek() {
Some(c) => c,
None => {
tokens.push((
ConditionToken::LineComment(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'line_comment;
}
};
if current == '\r' || current == '\n' {
tokens.push((
ConditionToken::LineComment(std::mem::take(
&mut buffer,
)),
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'line_comment;
} else {
buffer.push(current);
lexer.advance();
}
}
} else if lexer.take_pat("/*") {
'comment: loop {
if lexer.take_pat("*/") {
tokens.push((
ConditionToken::BlockComment {
str: std::mem::take(&mut buffer),
contains_eof: false,
},
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'comment;
}
match lexer.peek() {
Some(current) => {
if current == '\r' || current == '\n' {
tokens.push((
ConditionToken::BlockComment {
str: std::mem::take(&mut buffer),
contains_eof: true,
},
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'comment;
} else {
buffer.push(current);
lexer.advance();
}
}
None => {
tokens.push((
ConditionToken::BlockComment {
str: std::mem::take(&mut buffer),
contains_eof: true,
},
Span {
start: buffer_start,
end: lexer.position(),
},
));
break 'comment;
}
}
}
} else {
match match_conditional_punctuation(lexer) {
Some(t) => tokens.push((
t,
Span {
start: buffer_start,
end: lexer.position(),
},
)),
None => {
lexer.advance();
tokens.push((
ConditionToken::Invalid(current),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
}
}
} else if current.is_whitespace() {
lexer.advance();
} else {
lexer.advance();
tokens.push((
ConditionToken::Invalid(current),
Span {
start: buffer_start,
end: lexer.position(),
},
));
}
}
tokens
}
fn is_conditional_punctuation_start(c: &char) -> bool {
match c {
'=' | '+' | '-' | '*' | '/' | '%' | '>' | '<' | '!' | '~' | '&'
| '|' | '^' | '(' | ')' | ',' => true,
_ => false,
}
}
macro_rules! match_op {
($lexer:ident, $str:expr, $token:expr) => {
if $lexer.take_pat($str) {
return Some($token);
}
};
}
fn match_conditional_punctuation<C: super::Char>(
lexer: &mut Lexer<C>,
) -> Option<ConditionToken> {
match_op!(lexer, "==", ConditionToken::EqEq);
match_op!(lexer, "!=", ConditionToken::NotEq);
match_op!(lexer, ">=", ConditionToken::Ge);
match_op!(lexer, "<=", ConditionToken::Le);
match_op!(lexer, "&&", ConditionToken::AndAnd);
match_op!(lexer, "||", ConditionToken::OrOr);
match_op!(lexer, "<<", ConditionToken::LShift);
match_op!(lexer, ">>", ConditionToken::RShift);
match_op!(lexer, "(", ConditionToken::LParen);
match_op!(lexer, ")", ConditionToken::RParen);
match_op!(lexer, "+", ConditionToken::Add);
match_op!(lexer, "-", ConditionToken::Sub);
match_op!(lexer, "*", ConditionToken::Mul);
match_op!(lexer, "/", ConditionToken::Div);
match_op!(lexer, ">", ConditionToken::Gt);
match_op!(lexer, "<", ConditionToken::Lt);
match_op!(lexer, "!", ConditionToken::Not);
match_op!(lexer, "~", ConditionToken::Flip);
match_op!(lexer, "%", ConditionToken::Rem);
match_op!(lexer, "&", ConditionToken::And);
match_op!(lexer, "|", ConditionToken::Or);
match_op!(lexer, "^", ConditionToken::Xor);
match_op!(lexer, ",", ConditionToken::Comma);
None
}
pub(crate) fn concat_macro_body(
tokens: super::TokenStream,
span_encoding: SpanEncoding,
) -> (
super::TokenStream,
Vec<crate::diag::Syntax>,
Vec<crate::diag::Semantic>,
) {
use crate::diag::{PreprocDefineDiag, Semantic, Syntax};
let mut stack = Vec::new();
let mut syntax_diags = Vec::new();
let mut semantic_diags = Vec::new();
let mut tokens = tokens.into_iter();
while let Some(token) = tokens.next() {
if token.0 == super::Token::MacroConcat {
let previous = stack.pop();
let next = tokens.next();
match (previous, next) {
(Some(prev), Some(next)) => {
if next.0 == super::Token::MacroConcat {
syntax_diags.push(Syntax::PreprocDefine(
PreprocDefineDiag::TokenConcatMissingRHS(token.1),
));
stack.push(prev);
stack.push((
super::Token::Invalid('#'),
token.1.first_char(),
));
stack.push((
super::Token::Invalid('#'),
token.1.last_char(),
));
stack.push((
super::Token::Invalid('#'),
next.1.first_char(),
));
stack.push((
super::Token::Invalid('#'),
next.1.last_char(),
));
} else {
let mut new_string = prev.0.to_string();
new_string.push_str(&next.0.to_string());
let mut result = match span_encoding {
SpanEncoding::Utf8 => {
let mut lexer: Lexer<super::Utf8> =
Lexer::new(&new_string, span_encoding);
super::parse_tokens(&mut lexer, true, false)
}
SpanEncoding::Utf16 => {
let mut lexer: Lexer<super::Utf16> =
Lexer::new(&new_string, span_encoding);
super::parse_tokens(&mut lexer, true, false)
}
SpanEncoding::Utf32 => {
let mut lexer: Lexer<super::Utf32> =
Lexer::new(&new_string, span_encoding);
super::parse_tokens(&mut lexer, true, false)
}
};
if result.len() == 1 {
let (token, _) = result.remove(0);
stack.push((
token,
Span::new(prev.1.start, next.1.end),
));
} else {
semantic_diags.push(
Semantic::TokenConcatUnnecessary(token.1),
);
stack.push(prev);
stack.push(next);
}
}
}
(Some(prev), None) => {
syntax_diags.push(Syntax::PreprocDefine(
PreprocDefineDiag::TokenConcatMissingRHS(token.1),
));
stack.push(prev);
}
(None, Some(next)) => {
syntax_diags.push(Syntax::PreprocDefine(
PreprocDefineDiag::TokenConcatMissingLHS(token.1),
));
if next.0 == super::Token::MacroConcat {
syntax_diags.push(Syntax::PreprocDefine(
PreprocDefineDiag::TokenConcatMissingRHS(token.1),
));
stack.push((
super::Token::Invalid('#'),
token.1.first_char(),
));
stack.push((
super::Token::Invalid('#'),
token.1.last_char(),
));
stack.push((
super::Token::Invalid('#'),
next.1.first_char(),
));
stack.push((
super::Token::Invalid('#'),
next.1.last_char(),
));
}
stack.push(next);
}
(None, None) => {
syntax_diags.push(Syntax::PreprocDefine(
PreprocDefineDiag::TokenConcatMissingLHS(token.1),
));
syntax_diags.push(Syntax::PreprocDefine(
PreprocDefineDiag::TokenConcatMissingRHS(token.1),
));
stack.push((
super::Token::Invalid('#'),
token.1.first_char(),
));
stack.push((
super::Token::Invalid('#'),
token.1.last_char(),
));
}
}
} else {
stack.push(token);
}
}
(stack, syntax_diags, semantic_diags)
}