use std::mem::swap;
use std::rc::Rc;
use crate::classic::clvm::__type_compatibility__::{
Bytes, BytesFromType, Stream, UnvalidatedBytesFromType,
};
use crate::classic::clvm::casts::bigint_to_bytes_clvm;
use crate::classic::clvm::syntax_error::SyntaxErr;
use crate::classic::clvm_tools::ir::r#type::{IRRepr, NEW_BIT_CONSTANTS};
use crate::util::Number;
const BIT_CHAR_LIST: [u8; 16] = *b"0123456789abcdef";
pub struct IRReader {
stream: Stream,
language_flags: u32,
}
impl IRReader {
fn read(&mut self, n: usize) -> Bytes {
self.stream.read(n)
}
fn backup(&mut self, n: usize) {
let cur_seek = self.stream.get_seek();
if n > cur_seek {
self.stream.set_seek(0);
} else {
self.stream.set_seek((cur_seek - n) as i64);
}
}
pub fn read_expr(&mut self) -> Result<IRRepr, SyntaxErr> {
consume_object(self, self.language_flags)
}
pub fn new(s: Stream, flags: u32) -> Self {
IRReader {
stream: s,
language_flags: flags,
}
}
}
pub fn is_eol(chval: u8) -> bool {
chval == b'\r' || chval == b'\n'
}
pub fn is_space(chval: u8) -> bool {
chval == b' ' || chval == b'\t' || is_eol(chval)
}
pub fn consume_whitespace(s: &mut IRReader) {
let mut in_comment = false;
loop {
let b = s.read(1);
if b.length() == 0 {
return;
}
let ch = b.at(0);
if in_comment {
if is_eol(ch) {
in_comment = false;
} else {
continue;
}
}
if ch == b';' {
in_comment = true;
continue;
}
if is_space(ch) {
continue;
}
break;
}
s.backup(1);
}
pub fn consume_quoted(s: &mut IRReader, q: u8) -> Result<IRRepr, SyntaxErr> {
let starting_at = s.stream.get_seek() - 1;
let mut bs = false;
let mut qchars = vec![q];
loop {
let b = s.read(1);
if b.length() == 0 {
return Err(SyntaxErr::new(format!(
"unterminated string starting at {}: {}",
starting_at,
Bytes::new(Some(BytesFromType::Raw(qchars))).decode()
)));
}
if bs {
bs = false;
qchars.push(b.at(0));
} else if b.at(0) == b'\\' {
bs = true;
} else if b.at(0) == q {
break;
} else {
qchars.push(b.at(0));
}
}
qchars = qchars.iter().skip(1).copied().collect();
Ok(IRRepr::Quotes(Bytes::new(Some(BytesFromType::Raw(qchars)))))
}
pub fn is_hex(chars: &[u8]) -> bool {
chars.len() > 2 && chars[0] == b'0' && (chars[1] == b'x' || chars[1] == b'X')
}
pub fn is_dec(chars: &[u8]) -> bool {
let mut first = true;
for ch in chars.iter() {
if first {
first = false;
if *ch == b'-' {
continue;
}
}
if *ch > b'9' || *ch < b'0' {
return false;
}
}
true
}
fn new_bit_constants(language_flags: u32) -> bool {
(language_flags & NEW_BIT_CONSTANTS) != 0
}
pub fn bitwise_constant(bits: u32, chars: &[u8], force_byte: bool) -> Result<Vec<u8>, SyntaxErr> {
let radix = 1 << bits;
let mut current_bit: u32 = 0;
let mut bit_buffer: u16 = 0;
let mut out_data = Vec::new();
for ch in chars.iter().map(|c| c | 0x20).rev() {
if let Some(found) = BIT_CHAR_LIST
.iter()
.enumerate()
.take(radix)
.filter(|(_, the_char)| **the_char == ch)
.map(|(i, _)| i)
.next()
{
bit_buffer |= (found << current_bit) as u16;
current_bit += bits;
if current_bit > 8 {
current_bit -= 8;
out_data.push((bit_buffer & 0xff) as u8);
bit_buffer >>= 8;
}
} else {
return Err(SyntaxErr::new(format!(
"bitwise constant with {bits} bits, have char {}",
ch as char
)));
}
}
if (chars.len() > 1 || chars[0] != b'0' || force_byte)
&& (current_bit >= bits || (current_bit > 0 && (bit_buffer & 0xff) != 0))
{
out_data.push((bit_buffer & 0xff) as u8);
}
Ok(out_data.iter().rev().copied().collect())
}
pub fn interpret_atom_value(chars: &[u8], language_flags: u32) -> Result<IRRepr, SyntaxErr> {
let all_int_digits = || {
String::from_utf8(chars.to_vec())
.ok()
.and_then(|s| s.parse::<Number>().ok())
.map(|n| bigint_to_bytes_clvm(&n))
};
if chars.is_empty() {
Ok(IRRepr::Null)
} else if new_bit_constants(language_flags) && !chars.is_empty() && chars[0] == b'0' {
if chars.len() == 1 {
return Ok(IRRepr::Int(bigint_to_bytes_clvm(&Number::default()), true));
}
if chars.len() < 3 {
return Err(SyntaxErr::new(
"too short numeric constant starting with '0'".to_string(),
));
}
match (chars[1], all_int_digits()) {
(b'b', _) | (b'B', _) => Ok(IRRepr::Binary(Bytes::new(Some(BytesFromType::Raw(
bitwise_constant(1, &chars[2..], false)?,
))))),
(b'o', _) | (b'O', _) => Ok(IRRepr::Octal(Bytes::new(Some(BytesFromType::Raw(
bitwise_constant(3, &chars[2..], false)?,
))))),
(b'x', _) | (b'X', _) => Ok(IRRepr::Hex(Bytes::new(Some(BytesFromType::Raw(
bitwise_constant(4, &chars[2..], true)?,
))))),
(b'0', Some(decimal)) => Ok(IRRepr::Int(decimal, true)),
_ => Err(SyntaxErr::new(format!(
"malformed int or bit constant '{}'",
String::from_utf8_lossy(chars)
))),
}
} else if is_hex(chars) {
let mut string_bytes = if !chars.len().is_multiple_of(2) {
Bytes::new(Some(BytesFromType::Raw(vec![b'0'])))
} else {
Bytes::new(None)
};
string_bytes =
string_bytes.concat(&Bytes::new(Some(BytesFromType::Raw(chars[2..].to_vec()))));
Bytes::new_validated(Some(UnvalidatedBytesFromType::Hex(string_bytes.decode())))
.map(IRRepr::Hex)
} else if let Some(n) = all_int_digits() {
Ok(IRRepr::Int(n, true))
} else {
let string_bytes = Bytes::new(Some(BytesFromType::Raw(chars.to_vec())));
Ok(IRRepr::Symbol(string_bytes.decode()))
}
}
pub fn consume_atom(
s: &mut IRReader,
b: &Bytes,
language_flags: u32,
) -> Result<Option<IRRepr>, SyntaxErr> {
let mut result_vec = b.data().to_vec();
loop {
let b = s.read(1);
if b.length() == 0 {
if result_vec.is_empty() {
return Ok(None);
} else {
return interpret_atom_value(&result_vec, language_flags).map(Some);
}
}
if b.at(0) == b'(' || b.at(0) == b')' || is_space(b.at(0)) {
s.backup(1);
return interpret_atom_value(&result_vec, language_flags).map(Some);
}
result_vec.push(b.at(0));
}
}
fn enlist_ir(vec: &mut [IRRepr], tail: IRRepr) -> IRRepr {
let mut result = tail;
for i_reverse in 0..vec.len() {
let i = vec.len() - i_reverse - 1;
let mut next_head = IRRepr::Null;
swap(&mut vec[i], &mut next_head);
result = IRRepr::Cons(Rc::new(next_head), Rc::new(result));
}
result
}
pub fn consume_cons_body(s: &mut IRReader, language_flags: u32) -> Result<IRRepr, SyntaxErr> {
let mut result = vec![];
loop {
consume_whitespace(s);
let b = s.read(1);
if b.length() == 0 {
return Err(SyntaxErr::new("missing )".to_string()));
}
if b.at(0) == b')' {
return Ok(enlist_ir(&mut result, IRRepr::Null));
}
if b.at(0) == b'(' {
let v = consume_cons_body(s, language_flags)?;
result.push(v);
continue;
}
if b.at(0) == b'.' {
consume_whitespace(s);
let v = consume_object(s, language_flags)?;
consume_whitespace(s);
let b = s.read(1);
if b.length() == 0 || b.at(0) != b')' {
return Err(SyntaxErr::new("missing )".to_string()));
}
return Ok(enlist_ir(&mut result, v));
}
if b.at(0) == b'\"' || b.at(0) == b'\'' {
let v = consume_quoted(s, b.at(0))?;
result.push(v);
continue;
} else if let Some(f) = consume_atom(s, &b, language_flags)? {
result.push(f);
continue;
} else {
return Err(SyntaxErr::new("missing )".to_string()));
}
}
}
pub fn consume_object(s: &mut IRReader, language_flags: u32) -> Result<IRRepr, SyntaxErr> {
consume_whitespace(s);
let b = s.read(1);
if b.length() == 0 {
Ok(IRRepr::Null)
} else if b.at(0) == b'(' {
consume_cons_body(s, language_flags)
} else if b.at(0) == b'\"' || b.at(0) == b'\'' {
consume_quoted(s, b.at(0))
} else if let Some(ir) = consume_atom(s, &b, language_flags)? {
Ok(ir)
} else {
Err(SyntaxErr::new("empty stream".to_string()))
}
}
pub fn read_ir(s: &str, language_flags: u32) -> Result<IRRepr, SyntaxErr> {
let bytes_of_string = Bytes::new(Some(BytesFromType::Raw(s.as_bytes().to_vec())));
let stream = Stream::new(Some(bytes_of_string));
let mut reader = IRReader::new(stream, language_flags);
reader.read_expr()
}