dg_xch_core 1.0.5

Core library containing type/error definitions, CLVM tools, Consensus and Pool definitions
Documentation
pub mod keywords;
pub mod reader;

use crate::clvm::assemble::keywords::KEYWORD_TO_ATOM;
use crate::clvm::assemble::reader::{Reader, Token};
use crate::clvm::casts::bigint_to_bytes;
use crate::clvm::parser::sexp_to_bytes;
use crate::clvm::program::SerializedProgram;
use crate::clvm::sexp;
use crate::clvm::sexp::{AtomBuf, SExp};
use hex::decode;
use num_bigint::BigInt;
use std::io::{Error, ErrorKind};

pub fn assemble_text(s: &str) -> Result<SerializedProgram, Error> {
    let stream = s.as_bytes().to_vec();
    let mut reader = Reader::new(&stream);
    let sexp = tokenize_exp(&mut reader)?;
    Ok(SerializedProgram::from_bytes(&sexp_to_bytes(&sexp)?))
}

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_quote(chars: &[u8]) -> bool {
    chars.len() > 2
        && ((chars[0] == b'"' && chars[chars.len() - 1] == b'"')
            || (chars[0] == b'\'' && chars[chars.len() - 1] == b'\''))
}

pub fn tokenize_cons(tokens: &mut Reader) -> Result<SExp, Error> {
    if let Some(token) = tokens.next() {
        handle_cons(token, tokens)
    } else {
        Ok(sexp::NULL.clone())
    }
}

pub fn handle_cons(token: Token, tokens: &mut Reader) -> Result<SExp, Error> {
    if token.bytes == vec![b')'] {
        Ok(sexp::NULL.clone())
    } else {
        let token_idx = token.index;
        let first = handle_token(token, tokens)?;
        if let Some(token) = tokens.next() {
            let rest;
            if token.bytes == vec![b'.'] {
                rest = tokenize_exp(tokens)?;
                if let Some(token) = tokens.next() {
                    if token.bytes != vec![b')'] {
                        Err(Error::new(
                            ErrorKind::InvalidData,
                            format!("Illegal dot expression at position: {}", token.index),
                        ))
                    } else {
                        Ok(first.cons(rest)?)
                    }
                } else {
                    Err(Error::new(
                        ErrorKind::InvalidData,
                        format!(
                            "Unexpected end of source while parsing dot cons at: {}",
                            token.index
                        ),
                    ))
                }
            } else {
                rest = handle_cons(token, tokens)?;
                Ok(first.cons(rest)?)
            }
        } else {
            Err(Error::new(
                ErrorKind::InvalidData,
                format!(
                    "Unexpected end of source while parsing cons at: {}",
                    token_idx
                ),
            ))
        }
    }
}

pub fn handle_token(token: Token, tokens: &mut Reader) -> Result<SExp, Error> {
    if token.bytes == vec![b'('] {
        tokenize_cons(tokens)
    } else if token.bytes.is_empty() {
        Ok(sexp::NULL.clone())
    } else {
        match String::from_utf8(token.bytes.to_vec())
            .ok()
            .and_then(|s| s.parse::<BigInt>().ok())
            .and_then(|n| bigint_to_bytes(&n, true).ok())
        {
            Some(n) => Ok(SExp::Atom(AtomBuf::new(n))),
            None => {
                if is_hex(token.bytes) {
                    let mut bytes = if token.bytes.len() % 2 > 0 {
                        vec![b'0']
                    } else {
                        vec![]
                    };
                    bytes.extend(token.bytes[2..].to_vec());
                    let as_hex = String::from_utf8(bytes).map_err(|e| {
                        Error::new(
                            ErrorKind::InvalidInput,
                            format!("Invalid Hex Value: {:?}", e),
                        )
                    })?;
                    Ok(SExp::Atom(AtomBuf::new(decode(as_hex).map_err(|e| {
                        Error::new(
                            ErrorKind::InvalidInput,
                            format!("Invalid Hex Value: {:?}", e),
                        )
                    })?)))
                } else if is_quote(token.bytes) {
                    Ok(SExp::Atom(AtomBuf::new(
                        token.bytes[1..(token.bytes.len() - 1)].to_vec(),
                    )))
                } else {
                    let mut bytes = token.bytes;
                    if bytes[0] == b'#' {
                        bytes = &bytes[1..];
                    }
                    if let Ok(Some(kw)) =
                        String::from_utf8(bytes.to_vec()).map(|s| KEYWORD_TO_ATOM.get(&s))
                    {
                        Ok(SExp::Atom(AtomBuf::new(kw.clone())))
                    } else {
                        Ok(SExp::Atom(AtomBuf::new(bytes.to_vec())))
                    }
                }
            }
        }
    }
}

pub fn tokenize_exp(tokens: &mut Reader) -> Result<SExp, Error> {
    if let Some(token) = tokens.next() {
        handle_token(token, tokens)
    } else {
        Ok(sexp::NULL.clone())
    }
}