pub use crypt_macro::Cryptic;
use std::{
cell::LazyCell,
collections::HashMap,
ffi::OsString,
iter::Peekable,
path::{Path, PathBuf},
vec::IntoIter
};
use indexmap::IndexMap;
use oaken::{
Lexer, Registry,
TokenPosition, TracedToken,
TracedTokenStream,
util::{
numeric_state::{DecimalState, numeric_entry_rule},
string_state::StringState
}
};
#[derive(Debug, Clone)]
enum Token {
Identifier(String),
String(String),
Integer(i64),
Float(f64),
Boolean(bool), Null,
Import,
Assign, SingleTag, TagOpen, TagClose, BodyOpen, BodyClose, ListOpen, ListClose, Break, }
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Identifier(ident) => write!(f, "'{}'", ident),
Self::String(str) => write!(f, "\"{}\"", str),
Self::Integer(int) => write!(f, "{}", int),
Self::Float(fl) => write!(f, "{}", fl),
Self::Boolean(bool) => write!(f, "{}", bool),
Self::Null => write!(f, "null"),
Self::Import => write!(f, "!import"),
Self::Assign => write!(f, ": | ="),
Self::SingleTag => write!(f, "#"),
Self::TagOpen => write!(f, "("),
Self::TagClose => write!(f, ")"),
Self::ListOpen => write!(f, "["),
Self::ListClose => write!(f, "]"),
Self::BodyOpen => write!(f, "{{"),
Self::BodyClose => write!(f, "}}"),
Self::Break => write!(f, ","),
}
}
}
impl Token {
fn get_binding_power(&self) -> isize {
match self {
Self::Break => 1,
Self::Assign => 2,
_ => 0,
}
}
}
impl From<String> for Token {
fn from(value: String) -> Self {
Self::String(value)
}
}
macro_rules! from_int {
($($ty:ty)+) => {
$(
impl From<$ty> for Token {
fn from(value: $ty) -> Self {
Self::Integer(value as _)
}
}
)+
};
}
from_int!(i8 u8 i16 u16 i32 u32 i64 u64);
impl From<f32> for Token {
fn from(value: f32) -> Self {
Self::Float(value as _)
}
}
impl From<f64> for Token {
fn from(value: f64) -> Self {
Self::Float(value)
}
}
const TOKENS: LazyCell<Vec<(&'static str, Token, bool)>> =
LazyCell::new(||vec![
("true", Token::Boolean(true), false),
("false", Token::Boolean(false), false),
("null", Token::Null, false),
("!import", Token::Import, false),
("#", Token::SingleTag, true),
("(", Token::TagOpen, true),
(")", Token::TagClose, true),
("{", Token::BodyOpen, true),
("}", Token::BodyClose, true),
("[", Token::ListOpen, true),
("]", Token::ListClose, true),
(",", Token::Break, true),
("=", Token::Assign, true),
(":", Token::Assign, true),
]);
fn identifier_fallback(str: &String) -> Token {
Token::Identifier(str.clone())
}
pub struct CryptParserServer {
register: Registry<Token>,
opened_files: HashMap<OsString, TracedObject>,
}
impl CryptParserServer {
pub fn new() -> Self {
let mut reg= Registry::new(identifier_fallback);
for (kw, tk, br) in &*TOKENS {
reg = reg.new_keyword(kw, tk.clone(), *br).unwrap();
}
Self {
register: reg
.new_special_state('"', StringState::new('"'))
.add_new_entry_rule(numeric_entry_rule, DecimalState::default()),
opened_files: HashMap::new(),
}
}
pub fn open<P, T>(&mut self, path: P) -> Result<T, CryptError>
where
P: AsRef<Path>,
T: Cryptic,
{
let path_str = path.as_ref().canonicalize()?.as_os_str().to_os_string();
if let Some(built) = self.opened_files.get(&path_str) {
return T::cryptic(built.clone())
}
let tokens = self.tokenize(path)?;
let mut iter = tokens.into_iter().peekable();
let ast = parse_body(&mut iter)?;
let object = resolve(ast, self)?;
self.opened_files.insert(path_str, object.clone());
if let CryptObject::ObjectList(body) = &object.object
&& body.len() == 1 && matches!(&body.iter().next().unwrap().object, &CryptObject::ObjectBody(_))
{
let CryptObject::ObjectList(body) = object.object else { unreachable!() };
let object = body.into_iter().next().unwrap();
T::cryptic(object)
} else {
T::cryptic(object)
}
}
fn tokenize<P>(&self, path: P) -> std::io::Result<TracedTokenStream<Token>>
where
P: AsRef<Path>,
{
let mut lexer = Lexer::from(&self.register);
lexer.traced();
lexer.lex_file(path, None)?;
Ok(lexer.finish_as_trace())
}
}
#[derive(Debug)]
struct TracedNode {
position: TokenPosition,
node: Node,
}
#[derive(Debug)]
enum Node {
Identifier(String),
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Null,
Import(String),
Assignment(String, Box<TracedNode>),
Tag(Vec<TracedNode>, Box<TracedNode>),
Body(Vec<TracedNode>),
List(Vec<TracedNode>),
}
type TokenIterator = Peekable<IntoIter<TracedToken<Token>>>;
fn parse_body(iter: &mut TokenIterator) -> Result<TracedNode, CryptError> {
let mut items = Vec::new();
while let Some(_) = iter.peek() {
let item = parse(iter, 1)?;
items.push(item);
if let Some(t) = iter.peek() && matches!(t.get_type(), Token::Break) {
iter.next();
} else {
break;
}
}
if let Some(t) = iter.next() {
return Err(CryptError(Some(t.get_position().clone()), CryptErrorType::UnexpectedToken(t.into_type())))
}
Ok(TracedNode { position: TokenPosition { line: 0, column_start: 0, column_end: 0, path: None }, node: Node::List(items) })
}
fn parse(iter: &mut TokenIterator, current_binding_power: isize, ) -> Result<TracedNode, CryptError> {
let Some(current) = iter.next() else {
return Err(CryptError(None, CryptErrorType::UnexpectedEOF))
};
let mut left = parse_nud(iter, current)?;
while let Some(t) = iter.peek() && t.get_type().get_binding_power() > current_binding_power {
let current = iter.next().unwrap();
left = parse_led(iter, left, current)?;
}
Ok(left)
}
fn parse_nud(iter: &mut TokenIterator, current: TracedToken<Token>) -> Result<TracedNode, CryptError> {
let pos = current.get_position().clone();
let token = current.into_type();
let node = match token.clone() {
Token::Identifier(ident) => Node::Identifier(ident),
Token::String(str) => Node::String(str),
Token::Integer(int) => Node::Integer(int),
Token::Float(fl) => Node::Float(fl),
Token::Boolean(bool) => Node::Boolean(bool),
Token::Null => Node::Null,
Token::Import => {
let Some(t) = iter.next() else {
return Err(CryptError(None, CryptErrorType::UnexpectedEOF))
};
let path_token_pos = t.get_position().clone();
let t = t.into_type();
let Token::String(str) = t.clone() else {
return Err(CryptError(Some(path_token_pos), CryptErrorType::ExpectedOther("path string", t)))
};
Node::Import(str)
},
Token::SingleTag => {
if let Some(t) = iter.peek() && matches!(t.get_type(), Token::TagOpen) {
iter.next();
let mut tags = Vec::new();
while let Some(t) = iter.peek() && !matches!(t.get_type(), Token::TagClose) {
let tag = parse(iter, 1)?;
tags.push(tag);
if let Some(t) = iter.peek() && matches!(t.get_type(), Token::Break) {
iter.next();
} else {
break;
}
}
if let Some(t) = iter.next() && !matches!(t.get_type(), Token::TagClose) {
return Err(CryptError(Some(t.get_position().clone()), CryptErrorType::ExpectedOther("closing ')'", t.into_type())))
}
Node::Tag(tags, Box::new(parse(iter, 1)?))
} else {
let tag = parse(iter, 1)?;
Node::Tag(vec![tag], Box::new(parse(iter, 1)?))
}
}
Token::BodyOpen => {
let mut items = Vec::new();
while let Some(t) = iter.peek() && !matches!(t.get_type(), Token::BodyClose) {
let item = parse(iter, 1)?;
items.push(item);
if let Some(t) = iter.peek() && matches!(t.get_type(), Token::Break) {
iter.next();
} else {
break;
}
}
if let Some(t) = iter.next() && !matches!(t.get_type(), Token::BodyClose) {
return Err(CryptError(Some(t.get_position().clone()), CryptErrorType::ExpectedOther("closing '}'", t.into_type())))
}
Node::Body(items)
}
Token::ListOpen => {
let mut items = Vec::new();
while let Some(t) = iter.peek() && !matches!(t.get_type(), Token::ListClose) {
let item = parse(iter, 1)?;
items.push(item);
if let Some(t) = iter.peek() && matches!(t.get_type(), Token::Break) {
iter.next();
} else {
break;
}
}
if let Some(t) = iter.next() && !matches!(t.get_type(), Token::ListClose) {
return Err(CryptError(Some(t.get_position().clone()), CryptErrorType::ExpectedOther("closing ']'", t.into_type())))
}
Node::List(items)
}
_ => return Err(CryptError(Some(pos), CryptErrorType::UnexpectedToken(token)))
};
Ok(TracedNode { position: pos, node, })
}
fn parse_led(iter: &mut TokenIterator, left: TracedNode, current: TracedToken<Token>) -> Result<TracedNode, CryptError> {
let pos = current.get_position().clone();
let token = current.into_type();
let node = match token.clone() {
Token::Assign => {
let ident = match left.node {
Node::Identifier(ident) => ident,
Node::String(str) => str,
_ => return Err(CryptError(Some(left.position), CryptErrorType::ExpectedIdentifier))
};
let object = parse(iter, 1)?;
Node::Assignment(ident, Box::new(object))
}
_ => return Err(CryptError(Some(pos), CryptErrorType::UnexpectedToken(token)))
};
Ok(TracedNode { position: pos, node, })
}
fn resolve(node: TracedNode, server: &mut CryptParserServer) -> Result<TracedObject, CryptError> {
let pos = node.position;
let obj = match node.node {
Node::Identifier(ident) => CryptObject::FloatingIdentifier(ident),
Node::String(str) => CryptObject::ConstantString(str),
Node::Integer(int) => CryptObject::ConstantInteger(int),
Node::Float(fl) => CryptObject::ConstantFloat(fl),
Node::Boolean(bool) => CryptObject::ConstantBoolean(bool),
Node::Null => CryptObject::Null,
Node::Assignment(ident, item) => CryptObject::IdentifiedObject(ident, Box::new(resolve(*item, server)?)),
Node::Body(items) => {
let mut body = IndexMap::new();
for item in items {
let item = resolve(item, server)?;
match item.object {
CryptObject::IdentifiedObject(ident, item) => {
body.insert(ident, *item);
}
CryptObject::TaggedObject(tags, tagged_item) =>
if let CryptObject::IdentifiedObject(ident, tagged_item) = tagged_item.object {
body.insert(ident, TracedObject { position: item.position, object: CryptObject::TaggedObject(tags, tagged_item) });
} else {
return Err(CryptError(Some(item.position), CryptErrorType::ExpectedIdentifier))
},
_ => return Err(CryptError(Some(item.position), CryptErrorType::ExpectedIdentifier))
}
}
CryptObject::ObjectBody(body)
}
Node::List(items) => {
let mut resolved_items = Vec::new();
for item in items {
let item = resolve(item, server)?;
resolved_items.push(item);
}
CryptObject::ObjectList(resolved_items)
}
Node::Tag(tags, object) => {
let mut resolved_tags = Vec::new();
for tag in tags {
let tag = resolve(tag, server)?;
resolved_tags.push(tag);
}
CryptObject::TaggedObject(resolved_tags, Box::new(resolve(*object, server)?))
}
Node::Import(path) => {
let Some(current_path) = pos.path else {
return Err(CryptError(Some(pos), CryptErrorType::CannotImport))
};
let path_buf =
PathBuf::from(current_path.as_ref())
.canonicalize().unwrap(); let final_path = path_buf.parent().unwrap().join(path);
return server.open(final_path)
}
};
Ok(TracedObject { position: pos, object: obj })
}
#[derive(Debug, Clone)]
pub struct TracedObject {
position: TokenPosition,
object: CryptObject,
}
#[derive(Debug, Clone)]
enum CryptObject {
ConstantString(String),
ConstantInteger(i64),
ConstantFloat(f64),
ConstantBoolean(bool),
Null,
FloatingIdentifier(String),
IdentifiedObject(String, Box<TracedObject>),
TaggedObject (Vec<TracedObject>, Box<TracedObject>),
ObjectBody(IndexMap<String, TracedObject>),
ObjectList(Vec<TracedObject>),
}
#[derive(Debug, Clone)]
pub enum CrypticObject {
ConstantString(String),
ConstantInteger(i64),
ConstantFloat(f64),
ConstantBoolean(bool),
Null,
FloatingIdentifier(String),
IdentifiedObject(String, Box<CrypticObject>),
TaggedObject (Vec<CrypticObject>, Box<CrypticObject>),
ObjectBody(HashMap<String, CrypticObject>),
ObjectList(Vec<CrypticObject>),
}
#[derive(Debug, Clone)]
pub struct Tagged<T, O> {
pub tag: T,
pub object: O
}
#[derive(Debug, Clone)]
pub struct HashTagged<T, O> {
pub tags: HashMap<String, T>,
pub object: O
}
#[derive(Debug, Clone)]
pub struct ListTagged<T, O> {
pub tags: Vec<T>,
pub object: O
}
pub trait Cryptic: Sized {
fn cryptic(object: TracedObject) -> Result<Self, CryptError>;
}
impl TryInto<String> for TracedObject {
type Error = CryptError;
fn try_into(self) -> Result<String, Self::Error> {
match self.object {
CryptObject::ConstantString(str) => Ok(str),
CryptObject::FloatingIdentifier(ident) => Ok(ident),
CryptObject::ConstantInteger(int) => Ok(int.to_string()),
CryptObject::ConstantFloat(fl) => Ok(fl.to_string()),
CryptObject::ConstantBoolean(b) => Ok(b.to_string()),
CryptObject::Null => Ok(String::new()),
CryptObject::IdentifiedObject(_, obj) => (*obj).try_into(),
CryptObject::TaggedObject(_, obj) => (*obj).try_into(),
_ => Err(CryptError(Some(self.position), CryptErrorType::UnexpectedType))
}
}
}
impl TryInto<bool> for TracedObject {
type Error = CryptError;
fn try_into(self) -> Result<bool, Self::Error> {
match self.object {
CryptObject::ConstantString(str) => Ok(str != ""),
CryptObject::FloatingIdentifier(_) => Ok(true),
CryptObject::ConstantInteger(int) => Ok(int != 0),
CryptObject::ConstantFloat(fl) => Ok(fl != 0.0),
CryptObject::ConstantBoolean(b) => Ok(b),
CryptObject::Null => Ok(false),
CryptObject::IdentifiedObject(_, obj) => (*obj).try_into(),
CryptObject::TaggedObject(_, obj) => (*obj).try_into(),
_ => Err(CryptError(Some(self.position), CryptErrorType::UnexpectedType))
}
}
}
macro_rules! try_into_int {
($($ty:ty)+) => {
$(
impl TryInto<$ty> for TracedObject {
type Error = CryptError;
fn try_into(self) -> Result<$ty, Self::Error> {
match self.object {
CryptObject::ConstantInteger(int) => Ok(int as _),
CryptObject::ConstantFloat(fl) => Ok(fl as _),
CryptObject::ConstantBoolean(b) => Ok(if b {1 as _} else {0 as _}),
CryptObject::ConstantString(str) => match str.parse() {
Ok(i) => Ok(i),
Err(_) => Err(CryptError(Some(self.position), CryptErrorType::ParseError))
},
CryptObject::IdentifiedObject(_, item) => (*item).try_into(),
CryptObject::TaggedObject(_, obj) => (*obj).try_into(),
_ => Err(CryptError(Some(self.position), CryptErrorType::UnexpectedType))
}
}
}
)+
};
}
try_into_int!(i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64);
impl<T> TryInto<Vec<T>> for TracedObject
where
T: Cryptic
{
type Error = CryptError;
fn try_into(self) -> Result<Vec<T>, Self::Error> {
match self.object {
CryptObject::ObjectList(list) =>
list
.into_iter()
.map(|i| T::cryptic(i))
.collect(),
CryptObject::ObjectBody(body) =>
body
.into_values()
.into_iter()
.map(|i| T::cryptic(i))
.collect(),
CryptObject::IdentifiedObject(_, obj) => (*obj).try_into(),
CryptObject::TaggedObject(_, obj) => (*obj).try_into(),
_ => Ok(vec![T::cryptic(self)?])
}
}
}
impl<T> TryInto<HashMap<String, T>> for TracedObject
where
T: Cryptic
{
type Error = CryptError;
fn try_into(self) -> Result<HashMap<String, T>, Self::Error> {
match self.object {
CryptObject::ObjectBody(body) =>
body
.into_iter()
.map(|(k, i)| T::cryptic(i).map(|i| (k, i)))
.collect(),
CryptObject::ObjectList(list) => {
let mut body = HashMap::new();
let mut idx: usize = 0;
for item in list {
if let CryptObject::IdentifiedObject(ident, item) = item.object {
body.insert(ident, T::cryptic(*item)?);
} else {
body.insert(idx.to_string(), T::cryptic(item)?);
idx += 1;
}
}
Ok(body)
},
CryptObject::IdentifiedObject(ident, item) => Ok(vec![(ident, T::cryptic(*item)?)].into_iter().collect()),
CryptObject::TaggedObject(_, item) => (*item).try_into(),
_ => Err(CryptError(Some(self.position), CryptErrorType::UnexpectedType))
}
}
}
impl<T, O> TryInto<Tagged<T, O>> for TracedObject
where
T: Cryptic,
O: Cryptic
{
type Error = CryptError;
fn try_into(self) -> Result<Tagged<T, O>, Self::Error> {
match self.object {
CryptObject::TaggedObject(tags, obj) => {
let Some(tag) = tags.into_iter().next() else {
return Err(CryptError(Some(self.position), CryptErrorType::ExpectedTag))
};
let tag = T::cryptic(tag)?;
Ok(Tagged { tag: tag, object: O::cryptic(*obj)? })
}
_ => Err(CryptError(Some(self.position), CryptErrorType::UnexpectedType))
}
}
}
impl<T, O> TryInto<ListTagged<T, O>> for TracedObject
where
T: Cryptic,
O: Cryptic
{
type Error = CryptError;
fn try_into(self) -> Result<ListTagged<T, O>, Self::Error> {
match self.object {
CryptObject::TaggedObject(tags, obj) => {
let tags = tags.into_iter().map(|i| T::cryptic(i)).collect::<Result<Vec<T>, CryptError>>()?;
Ok(ListTagged { tags, object: O::cryptic(*obj)? })
}
_ => Ok(ListTagged { tags: Vec::new(), object: O::cryptic(self)? })
}
}
}
impl<T, O> TryInto<HashTagged<T, O>> for TracedObject
where
T: Cryptic,
O: Cryptic
{
type Error = CryptError;
fn try_into(self) -> Result<HashTagged<T, O>, Self::Error> {
match self.object {
CryptObject::TaggedObject(tags, item) => {
let mut built_tags = HashMap::new();
let mut idx: usize = 0;
for tag in tags {
if let CryptObject::IdentifiedObject(ident, item) = tag.object {
built_tags.insert(ident, T::cryptic(*item)?);
} else {
built_tags.insert(idx.to_string(), T::cryptic(tag)?);
idx += 1;
}
}
Ok(HashTagged { tags: built_tags, object: O::cryptic(*item)? })
}
_ => Ok(HashTagged { tags: HashMap::new(), object: O::cryptic(self)? })
}
}
}
impl<T> Cryptic for T
where
TracedObject: TryInto<T, Error = CryptError>,
{
fn cryptic(object: TracedObject) -> Result<Self, CryptError> {
object.try_into()
}
}
impl Cryptic for CryptObject {
fn cryptic(object: TracedObject) -> Result<Self, CryptError> {
Ok(object.object)
}
}
impl Cryptic for CrypticObject {
fn cryptic(object: TracedObject) -> Result<Self, CryptError> {
Ok(match object.object {
CryptObject::ConstantString(str) => CrypticObject::ConstantString(str),
CryptObject::ConstantInteger(int) => CrypticObject::ConstantInteger(int),
CryptObject::ConstantFloat(fl) => CrypticObject::ConstantFloat(fl),
CryptObject::ConstantBoolean(b) => CrypticObject::ConstantBoolean(b),
CryptObject::Null => CrypticObject::Null,
CryptObject::FloatingIdentifier(ident) => CrypticObject::FloatingIdentifier(ident),
CryptObject::IdentifiedObject(ident, item) => CrypticObject::IdentifiedObject(ident, Box::new(CrypticObject::cryptic(*item).unwrap())),
CryptObject::TaggedObject (tags, item) => CrypticObject::TaggedObject(tags.into_iter().map(|i| CrypticObject::cryptic(i).unwrap()).collect(), Box::new(CrypticObject::cryptic(*item).unwrap())),
CryptObject::ObjectBody(body) => CrypticObject::ObjectBody(body.into_iter().map(|(ident, item)| (ident, CrypticObject::cryptic(item).unwrap())).collect()),
CryptObject::ObjectList(list) => CrypticObject::ObjectList(list.into_iter().map(|i| CrypticObject::cryptic(i).unwrap()).collect()),
})
}
}
impl<T> Cryptic for Option<T>
where
T: Cryptic
{
fn cryptic(object: TracedObject) -> Result<Self, CryptError> {
match object.object {
CryptObject::Null => Ok(None),
_ => Ok(Some(Cryptic::cryptic(object)?))
}
}
}
impl Cryptic for TracedObject {
fn cryptic(object: TracedObject) -> Result<Self, CryptError> {
Ok(object)
}
}
#[derive(Debug)]
pub struct CryptError(Option<TokenPosition>, CryptErrorType);
impl CryptError {
pub const fn cannot_find_ident(name: &'static str) -> Self {
Self(None, CryptErrorType::NoItemOfIdentifier(name))
}
pub const fn not_enough_items() -> Self {
Self(None, CryptErrorType::NotEnoughItems)
}
pub fn invalid_enum_variant(variant: String) -> Self {
Self(None, CryptErrorType::InvalidEnumVariant(variant))
}
}
#[derive(Debug)]
enum CryptErrorType {
UnexpectedToken(Token),
ExpectedOther(&'static str, Token),
UnexpectedEOF,
ExpectedIdentifier,
CannotImport,
UnexpectedType,
ParseError,
ExpectedTag,
NoItemOfIdentifier(&'static str),
NotEnoughItems,
InvalidEnumVariant(String),
OsError(std::io::Error),
}
impl std::fmt::Display for CryptError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.1 {
CryptErrorType::UnexpectedToken(t) => writeln!(f, "Unexpected token: {}", t),
CryptErrorType::ExpectedOther(desc, t) => writeln!(f, "Unexpected token: {}, expected {}", t, desc),
CryptErrorType::UnexpectedEOF => writeln!(f, "Unexpected end of file"),
CryptErrorType::ExpectedIdentifier => writeln!(f, "Expected an identifier"),
CryptErrorType::CannotImport => writeln!(f, "Cannot import here"),
CryptErrorType::UnexpectedType => writeln!(f, "Unexpected type"),
CryptErrorType::ParseError => writeln!(f, "Failed to parse"),
CryptErrorType::ExpectedTag => writeln!(f, "Expected a tag"),
CryptErrorType::OsError(e) => writeln!(f, "{}", e),
CryptErrorType::NoItemOfIdentifier(ident) => write!(f, "Couldn't find item: {}", ident),
CryptErrorType::NotEnoughItems => write!(f, "Not enough items found"),
CryptErrorType::InvalidEnumVariant(v) => write!(f, "Invalid enum variant: {}", v),
}?;
if let Some(pos) = &self.0 {
writeln!(f, "at: {:?}", pos)?;
}
Ok(())
}
}
impl std::error::Error for CryptError {}
impl From<std::io::Error> for CryptError {
fn from(value: std::io::Error) -> Self {
CryptError(None, CryptErrorType::OsError(value))
}
}