use crate::{LexError, lexer::PtxToken, span};
use thiserror::Error;
#[cfg(debug_assertions)]
use stacker;
pub(crate) mod common;
pub(crate) mod function;
pub(crate) mod instruction;
pub(crate) mod module;
pub(crate) mod util;
pub(crate) mod variable;
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub const fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
}
impl From<std::ops::Range<usize>> for Span {
fn from(range: std::ops::Range<usize>) -> Self {
Span::new(range.start, range.end)
}
}
impl From<Span> for std::ops::Range<usize> {
fn from(span: Span) -> Self {
span.start..span.end
}
}
#[macro_export]
macro_rules! unexpected_token {
($span:expr, $expected:expr, $found:expr) => {
$crate::parser::PtxParseError {
kind: $crate::parser::ParseErrorKind::UnexpectedToken {
expected: $expected.iter().map(|s| s.to_string()).collect(),
found: $found,
},
span: $span,
}
};
}
macro_rules! reject_partial_mode {
($self:expr) => {
if $self.index.1.is_some() {
let span = $self
.tokens
.get($self.index.0)
.map_or(span!(0..0), |(_, s)| *s);
return Err($crate::parser::PtxParseError {
kind: $crate::parser::ParseErrorKind::InvalidModeForTokenMethod,
span,
});
}
};
}
macro_rules! no_candidate_match {
($self:expr, $candidates:expr) => {{
let span = $self
.tokens
.get($self.index.0)
.map_or(span!(0..0), |(_, s)| *s);
$crate::parser::PtxParseError {
kind: $crate::parser::ParseErrorKind::UnexpectedToken {
expected: $candidates.iter().map(|s| s.to_string()).collect(),
found: "no match".to_string(),
},
span,
}
}};
}
#[macro_export]
macro_rules! unexpected_value {
($span:expr, $expected:expr, $found:expr) => {
$crate::parser::PtxParseError {
kind: $crate::parser::ParseErrorKind::UnexpectedToken {
expected: $expected.iter().map(|s| s.to_string()).collect(),
found: $found.into(),
},
span: $span,
}
};
}
#[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),
#[error("cannot use token-based methods in partial mode")]
InvalidModeForTokenMethod,
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("parsing error at {span:?}: {kind}")]
pub struct PtxParseError {
pub kind: ParseErrorKind,
pub span: Span,
}
impl From<LexError> for PtxParseError {
fn from(err: LexError) -> Self {
PtxParseError {
kind: ParseErrorKind::InvalidLiteral("lexing failed".into()),
span: err.span,
}
}
}
pub type StreamPosition = (usize, Option<usize>);
pub struct PtxTokenStream<'a> {
tokens: &'a [(PtxToken, Span)],
index: StreamPosition,
}
impl<'a> PtxTokenStream<'a> {
pub fn new(tokens: &'a [(PtxToken, Span)]) -> Self {
Self {
tokens,
index: (0, None),
}
}
pub fn peek(&self) -> Result<&'a (PtxToken, Span), PtxParseError> {
reject_partial_mode!(self);
self.tokens.get(self.index.0).ok_or_else(|| {
let span = self.tokens.last().map_or(span!(0..0), |(_, s)| *s);
PtxParseError {
kind: ParseErrorKind::UnexpectedEof,
span,
}
})
}
pub fn peek_n(&self, offset: usize) -> Result<&'a (PtxToken, Span), PtxParseError> {
reject_partial_mode!(self);
self.tokens.get(self.index.0 + offset).ok_or_else(|| {
let span = self.tokens.last().map_or(span!(0..0), |(_, s)| *s);
PtxParseError {
kind: ParseErrorKind::UnexpectedEof,
span,
}
})
}
pub fn consume(&mut self) -> Result<&'a (PtxToken, Span), PtxParseError> {
reject_partial_mode!(self);
let token = self.peek()?;
self.index.0 += 1;
Ok(token)
}
pub fn consume_if<F>(&mut self, predicate: F) -> Option<&'a (PtxToken, Span)>
where
F: FnOnce(&PtxToken) -> bool,
{
if self.index.1.is_some() {
return None; }
if let Ok((token, _)) = self.peek() {
if predicate(token) {
self.index.0 += 1;
return self.tokens.get(self.index.0 - 1);
}
}
None
}
pub fn expect(&mut self, expected: &PtxToken) -> Result<&'a (PtxToken, Span), PtxParseError> {
reject_partial_mode!(self);
let token_pair = self.peek()?;
let (token, span) = token_pair;
if std::mem::discriminant(token) == std::mem::discriminant(expected) {
self.index.0 += 1;
Ok(token_pair)
} else {
Err(unexpected_token!(
*span,
&[format!("{:?}", expected)],
format!("{:?}", token)
))
}
}
fn expect_token_with_string<F>(
&mut self,
expected_name: &str,
extractor: F,
) -> Result<(String, Span), PtxParseError>
where
F: FnOnce(&PtxToken) -> Option<String>,
{
reject_partial_mode!(self);
let (token, span_ref) = self.peek()?;
if let Some(value) = extractor(token) {
let span = *span_ref;
self.index.0 += 1;
Ok((value, span))
} else {
Err(unexpected_token!(
*span_ref,
&[expected_name.to_string()],
format!("{:?}", token)
))
}
}
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 = Span::new(dot_span.start, id_span.end);
Ok((name, span))
}
fn match_string_internal(&mut self, pattern: &str) -> bool {
let start_pos = self.position();
let mut pattern_chars = pattern.chars().peekable();
loop {
if pattern_chars.peek().is_none() {
return true; }
if self.index.0 >= self.tokens.len() {
self.set_position(start_pos);
return false;
}
let (token, _span) = &self.tokens[self.index.0];
let token_str = token.as_str();
if let Some(char_offset) = self.index.1 {
let token_chars: Vec<char> = token_str.chars().collect();
if char_offset >= token_chars.len() {
self.index.0 += 1;
self.index.1 = Some(0);
continue;
}
let mut offset = char_offset;
while offset < token_chars.len() && pattern_chars.peek().is_some() {
if Some(&token_chars[offset]) == pattern_chars.peek() {
pattern_chars.next();
offset += 1;
} else {
self.set_position(start_pos);
return false;
}
}
self.index.1 = Some(offset);
} else {
let token_chars: Vec<char> = token_str.chars().collect();
let mut token_idx = 0;
while token_idx < token_chars.len() && pattern_chars.peek().is_some() {
if Some(&token_chars[token_idx]) == pattern_chars.peek() {
pattern_chars.next();
token_idx += 1;
} else {
self.set_position(start_pos);
return false;
}
}
if token_idx == token_chars.len() {
self.index.0 += 1;
} else if pattern_chars.peek().is_none() {
self.set_position(start_pos);
return false;
}
}
}
}
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.match_string_internal(candidate) {
return Ok(idx);
}
}
self.set_position(start_pos);
Err(no_candidate_match!(self, candidates))
}
pub fn expect_string(&mut self, expected: &str) -> Result<(), PtxParseError> {
let start_pos = self.position();
if self.match_string_internal(expected) {
Ok(())
} else {
self.set_position(start_pos);
Err(no_candidate_match!(self, &[expected]))
}
}
pub fn expect_complete(&mut self) -> Result<(), PtxParseError> {
if self.index.1.is_some() {
let span = self
.tokens
.get(self.index.0)
.map_or(span!(0..0), |(_, s)| *s);
return Err(PtxParseError {
kind: ParseErrorKind::InvalidModeForTokenMethod,
span,
});
}
Ok(())
}
pub fn with_partial_token_mode<F, R>(&mut self, f: F) -> Result<R, PtxParseError>
where
F: FnOnce(&mut PtxTokenStream) -> Result<R, PtxParseError>,
{
let start_index = self.index;
assert!(self.index.1.is_none(), "Already in partial mode");
self.index.1 = Some(0);
let result = f(self);
if let Some(char_offset) = self.index.1 {
if char_offset != 0 {
if let Some((token, span)) = self.tokens.get(self.index.0) {
if token.len() != char_offset {
self.index = start_index;
return Err(unexpected_token!(
*span,
&["fully consumed token".to_string()],
format!("partially consumed {:?}", token)
));
} else {
self.index.0 += 1;
}
}
}
}
self.index.1 = None;
result
}
pub fn try_with_span<F, R>(&mut self, f: F) -> Result<(R, Span), PtxParseError>
where
F: FnOnce(&mut PtxTokenStream) -> Result<R, PtxParseError>,
{
let start_pos = self.position();
match f(self) {
Ok(value) => {
let end_pos = self.position();
let span_start = self.offset_from_start(start_pos);
let span_end = self.offset_from_end(start_pos, end_pos).max(span_start);
Ok((value, Span::new(span_start, span_end)))
}
Err(err) => {
self.set_position(start_pos);
Err(err)
}
}
}
pub fn position(&self) -> StreamPosition {
self.index
}
pub fn set_position(&mut self, pos: StreamPosition) {
self.index = pos;
}
pub fn is_at_end(&self) -> bool {
self.index.0 >= self.tokens.len() && self.index.1.is_none()
}
pub fn current_span(&self) -> Span {
let offset = self.offset_from_start(self.index);
Span::new(offset, offset)
}
fn offset_from_start(&self, pos: StreamPosition) -> usize {
if let Some((_, span)) = self.tokens.get(pos.0) {
let token_offset = pos.1.unwrap_or(0);
return (span.start + token_offset).min(span.end);
}
self.tokens.last().map(|(_, span)| span.end).unwrap_or(0)
}
fn offset_from_end(&self, start: StreamPosition, end: StreamPosition) -> usize {
if start == end {
return self.offset_from_start(start);
}
if let Some(char_offset) = end.1 {
if let Some((_, span)) = self.tokens.get(end.0) {
return (span.start + char_offset).min(span.end);
}
} else if end.0 == 0 {
if let Some((_, span)) = self.tokens.get(0) {
return span.start;
}
} else if let Some((_, span)) = self.tokens.get(end.0 - 1) {
return span.end;
}
self.tokens
.last()
.map(|(_, span)| span.end)
.unwrap_or_else(|| self.offset_from_start(start))
}
}
pub trait PtxParser
where
Self: Sized,
{
fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError>;
}
pub fn parse_ptx(source: &str) -> Result<crate::r#type::module::Module, PtxParseError> {
#[cfg(debug_assertions)]
{
return stacker::grow(256 * 1024 * 1024, || parse_ptx_inner(source));
}
#[cfg(not(debug_assertions))]
{
parse_ptx_inner(source)
}
}
fn parse_ptx_inner(source: &str) -> Result<crate::r#type::module::Module, PtxParseError> {
use crate::{PtxTokenStream, tokenize, r#type::Module};
let tokens = tokenize(source)?;
let mut stream = PtxTokenStream::new(&tokens);
let (module, _) = Module::parse()(&mut stream)?;
if !stream.is_at_end() {
let pos = stream.position();
let remaining = tokens
.get(pos.0)
.map(|(tok, _)| format!("{:?}", tok))
.unwrap_or_else(|| "EOF".into());
return Err(PtxParseError {
kind: ParseErrorKind::UnexpectedToken {
expected: vec!["end of file".into()],
found: remaining,
},
span: stream.current_span(),
});
}
Ok(module)
}