use crate::lexer::{PtxToken, tokenize};
use thiserror::Error;
pub(crate) mod common;
pub(crate) mod function;
pub(crate) mod instruction;
pub(crate) mod module;
pub(crate) mod variable;
pub type Span = std::ops::Range<usize>;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseErrorKind {
#[error("unexpected token: expected one of {expected:?}, found {found}")]
UnexpectedToken {
expected: Vec<String>,
found: String,
},
#[error("unexpected end of input")]
UnexpectedEof,
#[error("invalid literal: {0}")]
InvalidLiteral(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("parsing error at {span:?}: {kind}")]
pub struct PtxParseError {
pub kind: ParseErrorKind,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamPosition {
pub index: usize,
pub char_offset: usize,
}
pub struct PtxTokenStream<'a> {
tokens: &'a [(PtxToken, Span)],
index: usize,
pub(crate) char_offset: usize,
}
impl<'a> PtxTokenStream<'a> {
pub fn new(tokens: &'a [(PtxToken, Span)]) -> Self {
Self {
tokens,
index: 0,
char_offset: 0,
}
}
pub fn peek(&self) -> Result<&'a (PtxToken, Span), PtxParseError> {
self.tokens.get(self.index).ok_or_else(|| {
let span = self.tokens.last().map_or(0..0, |(_, s)| s.clone());
PtxParseError {
kind: ParseErrorKind::UnexpectedEof,
span,
}
})
}
pub fn consume(&mut self) -> Result<&'a (PtxToken, Span), PtxParseError> {
let token = self.peek()?;
self.index += 1;
Ok(token)
}
pub fn expect(&mut self, expected: &PtxToken) -> Result<&'a (PtxToken, Span), PtxParseError> {
let token_pair = self.peek()?;
let (token, span) = token_pair;
if std::mem::discriminant(token) == std::mem::discriminant(expected) {
self.index += 1;
Ok(token_pair)
} else {
Err(PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: vec![format!("{:?}", expected)],
found: format!("{:?}", token),
},
span: span.clone(),
})
}
}
fn expect_token_with_string<F>(
&mut self,
expected_name: &str,
extractor: F,
) -> Result<(String, Span), PtxParseError>
where
F: FnOnce(&PtxToken) -> Option<String>,
{
let (token, span) = self.peek()?;
if let Some(value) = extractor(token) {
let span = span.clone();
self.index += 1;
Ok((value, span))
} else {
Err(PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: vec![expected_name.to_string()],
found: format!("{:?}", token),
},
span: span.clone(),
})
}
}
pub fn expect_identifier(&mut self) -> Result<(String, Span), PtxParseError> {
self.expect_token_with_string("Identifier", |token| {
if let PtxToken::Identifier(name) = token {
Some(name.clone())
} else {
None
}
})
}
pub fn expect_register(&mut self) -> Result<(String, Span), PtxParseError> {
self.expect_token_with_string("Register", |token| {
if let PtxToken::Register(name) = token {
Some(name.clone())
} else {
None
}
})
}
pub fn expect_directive(&mut self) -> Result<(String, Span), PtxParseError> {
let (_, dot_span) = self.expect(&PtxToken::Dot)?;
let (name, id_span) = self.expect_identifier()?;
let span = dot_span.start..id_span.end;
Ok((name, span))
}
pub fn expect_modifier(&mut self) -> Result<(String, Span), PtxParseError> {
self.expect_directive()
}
pub fn expect_double_colon(&mut self) -> Result<(), PtxParseError> {
self.expect(&PtxToken::Colon)?;
self.expect(&PtxToken::Colon)?;
Ok(())
}
pub fn expect_strings(&mut self, candidates: &[&str]) -> Result<usize, PtxParseError> {
let start_pos = self.position();
for (idx, candidate) in candidates.iter().enumerate() {
self.set_position(start_pos);
if self.try_match_string(candidate) {
return Ok(idx);
}
}
let (token, span) = self.peek()?;
Err(PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: candidates.iter().map(|s| s.to_string()).collect(),
found: format!("{:?}", token),
},
span: span.clone(),
})
}
pub fn expect_string(&mut self, expected: &str) -> Result<(), PtxParseError> {
if self.try_match_string(expected) {
Ok(())
} else {
let (token, span) = self.peek()?;
Err(PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: vec![expected.to_string()],
found: format!("{:?}", token),
},
span: span.clone(),
})
}
}
pub fn try_match_string(&mut self, pattern: &str) -> bool {
let start_pos = self.position();
let expected_tokens = match tokenize(pattern) {
Ok(tokens) => tokens,
Err(_) => {
return false;
}
};
for (expected_token, _) in expected_tokens {
match self.peek() {
Ok((actual_token, _)) => {
if let (PtxToken::Identifier(actual_id), expected_str) =
(actual_token, expected_token.as_str())
{
let remaining = &actual_id[self.char_offset..];
if remaining.starts_with(expected_str) {
let new_offset = self.char_offset + expected_str.len();
if new_offset == actual_id.len() {
self.index += 1;
self.char_offset = 0;
} else {
self.char_offset = new_offset;
}
continue;
}
}
if actual_token != &expected_token {
self.set_position(start_pos);
return false;
}
self.index += 1;
self.char_offset = 0;
}
Err(_) => {
self.set_position(start_pos);
return false;
}
}
}
true
}
pub fn check<F>(&self, predicate: F) -> bool
where
F: FnOnce(&PtxToken) -> bool,
{
self.tokens
.get(self.index)
.map_or(false, |(token, _)| predicate(token))
}
pub fn expect_complete(&self) -> Result<(), PtxParseError> {
if self.char_offset > 0 {
let span = self
.peek()
.map(|(_, s)| s.clone())
.unwrap_or(Span { start: 0, end: 0 });
Err(unexpected_value(
span,
&["complete token"],
format!("partial token at char offset {}", self.char_offset),
))
} else {
Ok(())
}
}
pub fn consume_if<F>(&mut self, predicate: F) -> Option<&'a (PtxToken, Span)>
where
F: FnOnce(&PtxToken) -> bool,
{
if self.check(predicate) {
self.index += 1;
self.tokens.get(self.index - 1)
} else {
None
}
}
pub fn position(&self) -> StreamPosition {
StreamPosition {
index: self.index,
char_offset: self.char_offset,
}
}
pub fn set_position(&mut self, pos: StreamPosition) {
self.index = pos.index;
self.char_offset = pos.char_offset;
}
pub fn is_at_end(&self) -> bool {
self.index >= self.tokens.len()
}
pub fn remaining(&self) -> &'a [(PtxToken, Span)] {
&self.tokens[self.index..]
}
pub fn peek_char_in_token(&self) -> Option<char> {
if self.index >= self.tokens.len() {
return None;
}
let (token, _) = &self.tokens[self.index];
let string = match token {
PtxToken::Identifier(s)
| PtxToken::DecimalInteger(s)
| PtxToken::HexInteger(s)
| PtxToken::BinaryInteger(s)
| PtxToken::OctalInteger(s) => s,
_ => return None,
};
string.chars().nth(self.char_offset)
}
pub fn consume_char_in_token(&mut self) -> Option<char> {
let ch = self.peek_char_in_token()?;
self.char_offset += 1;
if self.index < self.tokens.len() {
let (token, _) = &self.tokens[self.index];
let string = match token {
PtxToken::Identifier(s)
| PtxToken::DecimalInteger(s)
| PtxToken::HexInteger(s)
| PtxToken::BinaryInteger(s)
| PtxToken::OctalInteger(s) => s,
_ => "",
};
if self.char_offset >= string.len() {
self.index += 1;
self.char_offset = 0;
}
}
Some(ch)
}
pub fn expect_char_in_token(&mut self, expected: char) -> Result<char, PtxParseError> {
match self.peek_char_in_token() {
Some(ch) if ch == expected => {
self.consume_char_in_token();
Ok(ch)
}
Some(ch) => {
let span = if self.index < self.tokens.len() {
self.tokens[self.index].1.clone()
} else {
0..0
};
Err(PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: vec![format!("'{}'", expected)],
found: format!("'{}'", ch),
},
span,
})
}
None => {
let span = if self.index < self.tokens.len() {
self.tokens[self.index].1.clone()
} else {
0..0
};
Err(PtxParseError {
kind: ParseErrorKind::UnexpectedEof,
span,
})
}
}
}
}
pub trait PtxParser
where
Self: Sized,
{
fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError>;
}
pub fn parse_ptx(source: &str) -> Result<crate::r#type::module::Module, PtxParseError> {
let tokens = crate::lexer::tokenize(source).map_err(|err| PtxParseError {
kind: ParseErrorKind::InvalidLiteral("lexical error".into()),
span: err.span,
})?;
let mut stream = PtxTokenStream::new(&tokens);
let module = crate::r#type::module::Module::parse(&mut stream)?;
if !stream.is_at_end() {
let (token, span) = stream.peek()?;
return Err(unexpected_value(
span.clone(),
&["end of input"],
format!("{token:?}"),
));
}
Ok(module)
}
pub fn unexpected_value(span: Span, expected: &[&str], found: impl Into<String>) -> PtxParseError {
PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: expected.iter().map(|s| s.to_string()).collect(),
found: found.into(),
},
span,
}
}
pub(crate) fn invalid_literal(span: Span, message: impl Into<String>) -> PtxParseError {
PtxParseError {
kind: ParseErrorKind::InvalidLiteral(message.into()),
span,
}
}
pub(crate) fn expect_directive_value(
stream: &mut PtxTokenStream,
expected: &str,
) -> Result<(), PtxParseError> {
let (value, span) = stream.expect_directive()?;
if value == expected {
Ok(())
} else {
Err(unexpected_value(
span,
&[&format!(".{expected}")],
format!(".{value}"),
))
}
}
pub(crate) fn peek_directive(
stream: &mut PtxTokenStream,
) -> Result<Option<(String, Span)>, PtxParseError> {
if let Some((PtxToken::Dot, dot_span)) = stream.tokens.get(stream.index) {
if let Some((PtxToken::Identifier(value), id_span)) = stream.tokens.get(stream.index + 1) {
let span = dot_span.start..id_span.end;
return Ok(Some((value.clone(), span)));
}
}
Ok(None)
}