rogue-lang 0.0.0

A transpiler prioritizing simplicity while maintaining performance & efficiency
Documentation
use crate::{
    lexer::{Token, TokenType}, 
    utility::{
        res::Res,
        debug::Log
    }   
};

// LOCAL IMPORTS
mod grammar;
use grammar::{
    GRAMMAR,
    Rule,
    Item
};

// REFINED
#[derive(Clone)]
pub enum Refined {
    Token(Token),
    Node(Node)
}

// NODE
#[derive(Clone)]
pub struct Node {
    pub name: String,
    pub children: Vec<Refined>
}

pub struct SyntaxTree {
    children: Vec<Node>
}

fn print_refined(data: Refined, indent: &str, is_last: bool) {
    // └──
    // ├──
    //
    let marker = if is_last { "└──" } else { "├──" };

    print!("{indent}");
    print!("{marker}");

    match data {
        Refined::Token(token) => {
            print!("{token}");
            println!();
        },

        Refined::Node(node) => {
            print!("{}", node.name);
            println!();
            
            if node.children.is_empty() { return }

            let indent = format!("{indent}{}", if is_last {"    "} else {""});

            let mut index = 1;
            let last_index = node.children.len();

            for child in node.children {
                print_refined(child, &indent, index == last_index);

                index += 1;
            }
        },
    }
}

impl SyntaxTree {
    pub fn pretty_print(&self) {
        for node in &self.children {
            print_refined(
                Refined::Node(node.clone()), 
                "", 
                false
            )
        }
    }
}

// PARSER
struct Parser {
    tokens: Vec<Token>,

    index: usize,
    current: Option<Token>
}

impl Parser {
    #[inline]
    pub fn new(tokens: Vec<Token>) -> Self {
        Self { 
            tokens, 
            index: 0, 
            current: None
        }
    }

    fn next(&mut self) {
        self.index += 1;
        self.current = self.tokens.get(self.index).cloned();
    }

    #[inline]
    fn get_current(&self) -> Option<Token> {
        self.current.clone()
    }

    #[allow(clippy::only_used_in_recursion)]
    fn check_item(&self, item: &Item, current: &Token) -> bool {
        match item {
            Item::Token(tt) 
            | Item::IgnoredToken(tt) 
            | Item::OptionalToken(tt) => {
                return (tt.varies() && (&current.data == tt)) 
                || current.data.teq(tt)
            },

            Item::OneOf(_, _) => unimplemented!(),

            Item::Rule(r) 
            | Item::MinToManyRule(_, r)
            | Item::OptionalRule(r) => {
                return self.check_item(
                    &r.requests[0], 
                    current
                )
            }
        }
    }

    #[inline]
    fn check_rule(&mut self, rule: &Rule) -> bool {
        return self.check_item(
            &rule.requests[0], 
            &self.get_current().unwrap()
        );
    }

    fn process_request(&mut self, item: &Item) -> Option<Result<Refined, Log>> {
        let current = self.get_current().unwrap();

        match item {
            Item::Token(tt) => {
                if self.check_item(item, &current) {
                    let tok = current;

                    self.next();

                    return Some(Ok(
                        Refined::Token(tok)
                    ));
                } else {
                    panic!("Expected `{tt:?}` got `{:?}`", current.data)
                }
            },

            Item::Rule(r) => {
                let result = self.process_rule(r);

                match result {
                    Ok(node) => return Some(Ok(
                        Refined::Node(node)
                    )),

                    Err(log) => return Some(
                        Err(log)
                    ),
                }
            },

            Item::OptionalToken(_) => {
                if self.check_item(item, &current) {
                    let tok = current;

                    self.next();

                    return Some(Ok(
                        Refined::Token(tok)
                    ));
                } else {
                    return None
                }
            },

            Item::OptionalRule(r) => {
                if self.check_rule(r) {
                    let result = self.process_rule(r);

                    match result {
                        Ok(node) => return Some(Ok(
                            Refined::Node(node)
                        )),

                        Err(log) => return Some(
                            Err(log)
                        ),
                    }
                } else {
                    todo!()
                }
            },

            Item::OneOf(options, error) => {
                for option in *options {
                    if !self.check_item(option, &current) { continue }

                    let proc = self.process_request(option);

                    if proc.is_none() { // Skipped
                        continue
                    } else {
                        match proc.unwrap() {
                            Ok(result) => return Some(
                                Ok(result)
                            ),

                            Err(log) => return Some(
                                Err(log)
                            )
                        }
                    }
                }

                panic!("{error}");
            },

            Item::IgnoredToken(tt) => {
                if self.check_item(item, &current) {
                    self.next();

                    return None
                } else {
                    panic!("Expected `{tt:?}` got `{:?}`", current.data)
                }
            },

            Item::MinToManyRule(min, r) => {
                let mut all = Vec::<Refined>::new();

                if self.check_rule(r) {
                    let result = self.process_rule(r);

                    match result {
                        Ok(node) => all.push(Refined::Node(node)),

                        Err(log) => return Some(
                            Err(log)
                        ),
                    }
                } else {
                    if min < &1 { return None }

                    todo!()
                }

                let mut index = 1;

                'inf: loop {
                    if self.check_rule(r) {
                        let result = self.process_rule(r);
    
                        match result {
                            Ok(node) => all.push(Refined::Node(node)),
    
                            Err(log) => return Some(
                                Err(log)
                            ),
                        }
                    } else {
                        if &index >= min { break 'inf }
    
                        todo!()
                    }

                    index += 1;
                }

                return Some(Ok(
                    Refined::Node(Node {
                        name: r.name.to_owned(),
                        children: all
                    })
                ))
            },
        }
    }

    fn process_rule(&mut self, rule: &Rule) -> Result<Node, Log> {
        let mut processed = Vec::<Refined>::new();

        for request in rule.requests {
            let proc = self.process_request(request);

            if proc.is_none() { // Skipped
                continue
            } else {
                let unwrap = proc.unwrap();

                match unwrap {
                    Ok(result) => processed.push(result),
                    Err(log) => return Err(log)
                }
            }
        }

        return Ok(Node { 
            name: rule.name.to_owned(), 
            children: processed 
        })
    }

    pub fn parse(&mut self, filename: String, source: String) -> Res<SyntaxTree> {
        let mut res = Res::<SyntaxTree>::new(filename, source);

        let mut nodes = Vec::<Node>::new();

        self.current = self.tokens.get(self.index).cloned();

        while self.current.is_some() {
            for rule in GRAMMAR {
                if !self.check_rule(rule) { continue }
    
                let result = self.process_rule(rule);
    
                match result {
                    Ok(node) => nodes.push(node),
                    Err(log) => { res.fail(log); break }
                }
            };

            if let Some(current) = &self.current {
                if current.data == TokenType::Eof { break }
            }
        }

        if !res.has_errors() {
            res.success(SyntaxTree {
                children: nodes
            })
        }

        return res
    }
}

// MAIN
pub fn parse(filename: impl Into<String>, source: impl Into<String>, tokens: &Vec<Token>) -> Res<SyntaxTree> {
    if tokens.is_empty() { panic!("No tokens to parse") }
    
    // Init
    let filename = filename.into();
    let source = source.into();

    // Parse
    let mut parser = Parser::new(tokens.clone());

    return parser.parse(filename, source)
}