summaryrefslogtreecommitdiffstats
path: root/third_party/rust/naga/src/front/glsl/lex.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/naga/src/front/glsl/lex.rs')
-rw-r--r--third_party/rust/naga/src/front/glsl/lex.rs380
1 files changed, 380 insertions, 0 deletions
diff --git a/third_party/rust/naga/src/front/glsl/lex.rs b/third_party/rust/naga/src/front/glsl/lex.rs
new file mode 100644
index 0000000000..57e86a4c2c
--- /dev/null
+++ b/third_party/rust/naga/src/front/glsl/lex.rs
@@ -0,0 +1,380 @@
+use super::parser::Token;
+use super::{preprocess::LinePreProcessor, token::TokenMetadata, types::parse_type};
+use std::{iter::Enumerate, str::Lines};
+
+fn _consume_str<'a>(input: &'a str, what: &str) -> Option<&'a str> {
+ if input.starts_with(what) {
+ Some(&input[what.len()..])
+ } else {
+ None
+ }
+}
+
+fn consume_any(input: &str, what: impl Fn(char) -> bool) -> (&str, &str, usize) {
+ let pos = input.find(|c| !what(c)).unwrap_or_else(|| input.len());
+ let (o, i) = input.split_at(pos);
+ (o, i, pos)
+}
+
+#[derive(Clone, Debug)]
+pub struct Lexer<'a> {
+ lines: Enumerate<Lines<'a>>,
+ input: String,
+ line: usize,
+ offset: usize,
+ inside_comment: bool,
+ pub pp: LinePreProcessor,
+}
+
+impl<'a> Lexer<'a> {
+ pub fn consume_token(&mut self) -> Option<Token> {
+ let start = self
+ .input
+ .find(|c: char| !c.is_whitespace())
+ .unwrap_or_else(|| self.input.chars().count());
+ let input = &self.input[start..];
+
+ let mut chars = input.chars();
+ let cur = match chars.next() {
+ Some(c) => c,
+ None => {
+ self.input = self.input[start..].into();
+ return None;
+ }
+ };
+ let mut meta = TokenMetadata {
+ line: 0,
+ chars: start..start + 1,
+ };
+ let mut consume_all = false;
+ let token = match cur {
+ ':' => Some(Token::Colon(meta)),
+ ';' => Some(Token::Semicolon(meta)),
+ ',' => Some(Token::Comma(meta)),
+ '.' => Some(Token::Dot(meta)),
+
+ '(' => Some(Token::LeftParen(meta)),
+ ')' => Some(Token::RightParen(meta)),
+ '{' => Some(Token::LeftBrace(meta)),
+ '}' => Some(Token::RightBrace(meta)),
+ '[' => Some(Token::LeftBracket(meta)),
+ ']' => Some(Token::RightBracket(meta)),
+ '<' | '>' => {
+ let n1 = chars.next();
+ let n2 = chars.next();
+ match (cur, n1, n2) {
+ ('<', Some('<'), Some('=')) => {
+ meta.chars.end = start + 3;
+ Some(Token::LeftAssign(meta))
+ }
+ ('>', Some('>'), Some('=')) => {
+ meta.chars.end = start + 3;
+ Some(Token::RightAssign(meta))
+ }
+ ('<', Some('<'), _) => {
+ meta.chars.end = start + 2;
+ Some(Token::LeftOp(meta))
+ }
+ ('>', Some('>'), _) => {
+ meta.chars.end = start + 2;
+ Some(Token::RightOp(meta))
+ }
+ ('<', Some('='), _) => {
+ meta.chars.end = start + 2;
+ Some(Token::LeOp(meta))
+ }
+ ('>', Some('='), _) => {
+ meta.chars.end = start + 2;
+ Some(Token::GeOp(meta))
+ }
+ ('<', _, _) => Some(Token::LeftAngle(meta)),
+ ('>', _, _) => Some(Token::RightAngle(meta)),
+ _ => None,
+ }
+ }
+ '0'..='9' => {
+ let (number, _, pos) = consume_any(input, |c| (c >= '0' && c <= '9' || c == '.'));
+ if number.find('.').is_some() {
+ if (
+ chars.next().map(|c| c.to_lowercase().next().unwrap()),
+ chars.next().map(|c| c.to_lowercase().next().unwrap()),
+ ) == (Some('l'), Some('f'))
+ {
+ meta.chars.end = start + pos + 2;
+ Some(Token::DoubleConstant((meta, number.parse().unwrap())))
+ } else {
+ meta.chars.end = start + pos;
+
+ Some(Token::FloatConstant((meta, number.parse().unwrap())))
+ }
+ } else {
+ meta.chars.end = start + pos;
+ Some(Token::IntConstant((meta, number.parse().unwrap())))
+ }
+ }
+ 'a'..='z' | 'A'..='Z' | '_' => {
+ let (word, _, pos) = consume_any(input, |c| c.is_ascii_alphanumeric() || c == '_');
+ meta.chars.end = start + pos;
+ match word {
+ "layout" => Some(Token::Layout(meta)),
+ "in" => Some(Token::In(meta)),
+ "out" => Some(Token::Out(meta)),
+ "uniform" => Some(Token::Uniform(meta)),
+ "flat" => Some(Token::Interpolation((meta, crate::Interpolation::Flat))),
+ "noperspective" => {
+ Some(Token::Interpolation((meta, crate::Interpolation::Linear)))
+ }
+ "smooth" => Some(Token::Interpolation((
+ meta,
+ crate::Interpolation::Perspective,
+ ))),
+ "centroid" => {
+ Some(Token::Interpolation((meta, crate::Interpolation::Centroid)))
+ }
+ "sample" => Some(Token::Interpolation((meta, crate::Interpolation::Sample))),
+ // values
+ "true" => Some(Token::BoolConstant((meta, true))),
+ "false" => Some(Token::BoolConstant((meta, false))),
+ // jump statements
+ "continue" => Some(Token::Continue(meta)),
+ "break" => Some(Token::Break(meta)),
+ "return" => Some(Token::Return(meta)),
+ "discard" => Some(Token::Discard(meta)),
+ // selection statements
+ "if" => Some(Token::If(meta)),
+ "else" => Some(Token::Else(meta)),
+ "switch" => Some(Token::Switch(meta)),
+ "case" => Some(Token::Case(meta)),
+ "default" => Some(Token::Default(meta)),
+ // iteration statements
+ "while" => Some(Token::While(meta)),
+ "do" => Some(Token::Do(meta)),
+ "for" => Some(Token::For(meta)),
+ // types
+ "void" => Some(Token::Void(meta)),
+ word => {
+ let token = match parse_type(word) {
+ Some(t) => Token::TypeName((meta, t)),
+ None => Token::Identifier((meta, String::from(word))),
+ };
+ Some(token)
+ }
+ }
+ }
+ '+' | '-' | '&' | '|' | '^' => {
+ let next = chars.next();
+ if next == Some(cur) {
+ meta.chars.end = start + 2;
+ match cur {
+ '+' => Some(Token::IncOp(meta)),
+ '-' => Some(Token::DecOp(meta)),
+ '&' => Some(Token::AndOp(meta)),
+ '|' => Some(Token::OrOp(meta)),
+ '^' => Some(Token::XorOp(meta)),
+ _ => None,
+ }
+ } else {
+ match next {
+ Some('=') => {
+ meta.chars.end = start + 2;
+ match cur {
+ '+' => Some(Token::AddAssign(meta)),
+ '-' => Some(Token::SubAssign(meta)),
+ '&' => Some(Token::AndAssign(meta)),
+ '|' => Some(Token::OrAssign(meta)),
+ '^' => Some(Token::XorAssign(meta)),
+ _ => None,
+ }
+ }
+ _ => match cur {
+ '+' => Some(Token::Plus(meta)),
+ '-' => Some(Token::Dash(meta)),
+ '&' => Some(Token::Ampersand(meta)),
+ '|' => Some(Token::VerticalBar(meta)),
+ '^' => Some(Token::Caret(meta)),
+ _ => None,
+ },
+ }
+ }
+ }
+
+ '%' | '!' | '=' => match chars.next() {
+ Some('=') => {
+ meta.chars.end = start + 2;
+ match cur {
+ '%' => Some(Token::ModAssign(meta)),
+ '!' => Some(Token::NeOp(meta)),
+ '=' => Some(Token::EqOp(meta)),
+ _ => None,
+ }
+ }
+ _ => match cur {
+ '%' => Some(Token::Percent(meta)),
+ '!' => Some(Token::Bang(meta)),
+ '=' => Some(Token::Equal(meta)),
+ _ => None,
+ },
+ },
+
+ '*' => match chars.next() {
+ Some('=') => {
+ meta.chars.end = start + 2;
+ Some(Token::MulAssign(meta))
+ }
+ Some('/') => {
+ meta.chars.end = start + 2;
+ Some(Token::CommentEnd((meta, ())))
+ }
+ _ => Some(Token::Star(meta)),
+ },
+ '/' => {
+ match chars.next() {
+ Some('=') => {
+ meta.chars.end = start + 2;
+ Some(Token::DivAssign(meta))
+ }
+ Some('/') => {
+ // consume rest of line
+ consume_all = true;
+ None
+ }
+ Some('*') => {
+ meta.chars.end = start + 2;
+ Some(Token::CommentStart((meta, ())))
+ }
+ _ => Some(Token::Slash(meta)),
+ }
+ }
+ '#' => {
+ if self.offset == 0 {
+ let mut input = chars.as_str();
+
+ // skip whitespace
+ let word_start = input
+ .find(|c: char| !c.is_whitespace())
+ .unwrap_or_else(|| input.chars().count());
+ input = &input[word_start..];
+
+ let (word, _, pos) = consume_any(input, |c| c.is_alphanumeric() || c == '_');
+ meta.chars.end = start + word_start + 1 + pos;
+ match word {
+ "version" => Some(Token::Version(meta)),
+ w => Some(Token::Unknown((meta, w.into()))),
+ }
+
+ //TODO: preprocessor
+ // if chars.next() == Some(cur) {
+ // (Token::TokenPasting, chars.as_str(), start, start + 2)
+ // } else {
+ // (Token::Preprocessor, input, start, start + 1)
+ // }
+ } else {
+ Some(Token::Unknown((meta, '#'.to_string())))
+ }
+ }
+ '~' => Some(Token::Tilde(meta)),
+ '?' => Some(Token::Question(meta)),
+ ch => Some(Token::Unknown((meta, ch.to_string()))),
+ };
+ if let Some(token) = token {
+ let skip_bytes = input
+ .chars()
+ .take(token.extra().chars.end - start)
+ .fold(0, |acc, c| acc + c.len_utf8());
+ self.input = input[skip_bytes..].into();
+ Some(token)
+ } else {
+ if consume_all {
+ self.input = "".into();
+ } else {
+ self.input = self.input[start..].into();
+ }
+ None
+ }
+ }
+
+ pub fn new(input: &'a str) -> Self {
+ let mut lexer = Lexer {
+ lines: input.lines().enumerate(),
+ input: "".to_string(),
+ line: 0,
+ offset: 0,
+ inside_comment: false,
+ pp: LinePreProcessor::new(),
+ };
+ lexer.next_line();
+ lexer
+ }
+
+ fn next_line(&mut self) -> bool {
+ if let Some((line, input)) = self.lines.next() {
+ let mut input = String::from(input);
+
+ while input.ends_with('\\') {
+ if let Some((_, next)) = self.lines.next() {
+ input.pop();
+ input.push_str(next);
+ } else {
+ break;
+ }
+ }
+
+ if let Ok(processed) = self.pp.process_line(&input) {
+ self.input = processed.unwrap_or_default();
+ self.line = line;
+ self.offset = 0;
+ true
+ } else {
+ //TODO: handle preprocessor error
+ false
+ }
+ } else {
+ false
+ }
+ }
+
+ #[must_use]
+ pub fn next(&mut self) -> Option<Token> {
+ let token = self.consume_token();
+
+ if let Some(mut token) = token {
+ let meta = token.extra_mut();
+ let end = meta.chars.end;
+ meta.line = self.line;
+ meta.chars.start += self.offset;
+ meta.chars.end += self.offset;
+ self.offset += end;
+ if !self.inside_comment {
+ match token {
+ Token::CommentStart(_) => {
+ self.inside_comment = true;
+ self.next()
+ }
+ _ => Some(token),
+ }
+ } else {
+ if let Token::CommentEnd(_) = token {
+ self.inside_comment = false;
+ }
+ self.next()
+ }
+ } else {
+ if !self.next_line() {
+ return None;
+ }
+ self.next()
+ }
+ }
+
+ // #[must_use]
+ // pub fn peek(&mut self) -> Option<Token> {
+ // self.clone().next()
+ // }
+}
+
+impl<'a> Iterator for Lexer<'a> {
+ type Item = Token;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.next()
+ }
+}