use std::borrow::Cow;
use crate::pdf_enum;
use crate::postscript::{PostScriptError, PostScriptResult};
#[derive(Debug)]
pub(crate) struct PostScriptFunctionLexer {
buffer: Box<[u8]>,
cursor: usize,
}
#[derive(Debug)]
pub(crate) enum PostScriptFunctionToken {
Operator(PostScriptFunctionOperator),
Real(f32),
Integer(i32),
OpenCurlyBrace,
CloseCurlyBrace,
}
pdf_enum!(
#[derive(Debug)]
pub(crate) enum PostScriptFunctionOperator {
Abs = "abs",
Add = "add",
Atan = "atan",
Ceiling = "ceiling",
Cos = "cos",
Cvi = "cvi",
Cvr = "cvr",
Div = "div",
Exp = "exp",
Floor = "floor",
Idiv = "idiv",
Ln = "ln",
Log = "log",
Mod = "mod",
Mul = "mul",
Neg = "neg",
Round = "round",
Sin = "sin",
Sqrt = "sqrt",
Sub = "sub",
Truncate = "truncate",
And = "and",
Bitshift = "bitshift",
Eq = "eq",
False = "false",
Ge = "ge",
Gt = "gt",
Le = "le",
Lt = "lt",
Ne = "ne",
Not = "not",
Or = "or",
True = "true",
Xor = "xor",
If = "if",
Ifelse = "ifelse",
Copy = "copy",
Dup = "dup",
Exch = "exch",
Index = "index",
Pop = "pop",
Roll = "roll",
}
);
fn ident_token_from_bytes(bytes: &[u8]) -> PostScriptResult<PostScriptFunctionToken> {
Ok(match bytes {
b"abs" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Abs),
b"add" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Add),
b"atan" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Atan),
b"ceiling" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Ceiling),
b"cos" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Cos),
b"cvi" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Cvi),
b"cvr" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Cvr),
b"div" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Div),
b"exp" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Exp),
b"floor" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Floor),
b"idiv" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Idiv),
b"ln" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Ln),
b"log" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Log),
b"mod" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Mod),
b"mul" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Mul),
b"neg" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Neg),
b"round" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Round),
b"sin" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Sin),
b"sqrt" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Sqrt),
b"sub" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Sub),
b"truncate" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Truncate),
b"and" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::And),
b"bitshift" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Bitshift),
b"eq" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Eq),
b"false" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::False),
b"ge" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Ge),
b"gt" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Gt),
b"le" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Le),
b"lt" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Lt),
b"ne" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Ne),
b"not" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Not),
b"or" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Or),
b"true" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::True),
b"xor" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Xor),
b"if" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::If),
b"ifelse" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Ifelse),
b"copy" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Copy),
b"dup" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Dup),
b"exch" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Exch),
b"index" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Index),
b"pop" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Pop),
b"roll" => PostScriptFunctionToken::Operator(PostScriptFunctionOperator::Roll),
_ => {
return Err(PostScriptError::ParseError(Cow::Owned(format!(
"Unrecognized operator: {:?}",
bytes
))))
}
})
}
impl PostScriptFunctionLexer {
pub fn new(buffer: Box<[u8]>) -> Self {
Self { buffer, cursor: 0 }
}
fn skip_whitespace(&mut self) {
while let Some(b) = self.peek_byte() {
if b.is_ascii_whitespace() {
self.next_byte();
}
}
}
fn next_byte(&mut self) -> Option<u8> {
let b = self.buffer.get(self.cursor)?;
self.cursor += 1;
Some(*b)
}
fn peek_byte(&mut self) -> Option<u8> {
self.buffer.get(self.cursor).cloned()
}
fn lex_ident(&mut self) -> PostScriptResult<PostScriptFunctionToken> {
let start = self.cursor;
while let Some(b) = self.peek_byte() {
if !b.is_ascii_alphabetic() {
break;
}
self.next_byte();
}
ident_token_from_bytes(&self.buffer[start..=self.cursor])
}
fn lex_whole_number(&mut self) {
while let Some(b) = self.peek_byte() {
if !b.is_ascii_digit() {
break;
}
self.next_byte();
}
}
fn consume_if_next_byte_is(&mut self, b: u8) -> bool {
if self.peek_byte() == Some(b) {
self.next_byte();
return true;
}
false
}
fn lex_number(&mut self) -> PostScriptResult<PostScriptFunctionToken> {
let start = self.cursor;
self.consume_if_next_byte_is(b'-');
self.lex_whole_number();
if self.consume_if_next_byte_is(b'.') {
self.lex_whole_number();
return Ok(PostScriptFunctionToken::Real(fast_float::parse(
&self.buffer[start..self.cursor],
)?));
}
Ok(PostScriptFunctionToken::Integer(parse_integer(
&self.buffer[start..self.cursor],
)))
}
fn next_token(&mut self) -> Option<PostScriptResult<PostScriptFunctionToken>> {
Some(match self.peek_byte()? {
b'0'..=b'9' | b'-' | b'.' => self.lex_number(),
b'a'..=b'z' | b'A'..=b'Z' => self.lex_ident(),
b'{' => Ok(PostScriptFunctionToken::OpenCurlyBrace),
b'}' => Ok(PostScriptFunctionToken::CloseCurlyBrace),
b => todo!("unexpected token start {:?}", b),
})
}
}
fn parse_integer(bytes: &[u8]) -> i32 {
let mut n = 0;
for b in bytes {
n *= 10;
n += *b as i32;
}
n
}
impl Iterator for PostScriptFunctionLexer {
type Item = PostScriptResult<PostScriptFunctionToken>;
fn next(&mut self) -> Option<Self::Item> {
self.next_token()
}
}