netter 0.3.1

Netter is a CLI tool for fast and easy server startup!
use std::fmt;
use crate::core::language::operators::{
    Token,
    TokenType,
};

pub struct Lexer {
    pub(crate) input: Vec<char>,
    pub(crate) position: usize,
    pub(crate) line: usize,
    pub(crate) column: usize,
}

impl Lexer {
    pub fn new(input: &str) -> Self {
        Lexer {
            input: input.chars().collect(),
            position: 0,
            line: 1,
            column: 1,
        }
    }

    pub fn peek(&self) -> Option<char> {
        if self.position < self.input.len() {
            Some(self.input[self.position])
        } else {
            None
        }
    }

    pub fn peek_next(&self) -> Option<char> {
        if self.position + 1 < self.input.len() {
            Some(self.input[self.position + 1])
        } else {
            None
        }
    }

    pub fn consume(&mut self) -> Option<char> {
        if self.position < self.input.len() {
            let ch = self.input[self.position];
            self.position += 1;
            
            if ch == '\n' {
                self.line += 1;
                self.column = 1;
            } else {
                self.column += 1;
            }
            
            Some(ch)
        } else {
            None
        }
    }

    pub fn skip_whitespace(&mut self) {
        while let Some(ch) = self.peek() {
            if ch.is_whitespace() {
                self.consume();
            } else {
                break;
            }
        }
    }

    pub fn read_identifier(&mut self) -> String {
        let mut identifier = String::new();
        
        while let Some(ch) = self.peek() {
            if ch.is_alphanumeric() || ch == '_' || ch == '{' || ch == '}' {
                identifier.push(ch);
                self.consume();
            } else {
                break;
            }
        }
        
        identifier
    }

    pub fn read_string(&mut self) -> Result<String, String> {
        self.consume();
        
        let mut string = String::new();
        
        while let Some(ch) = self.peek() {
            if ch == '"' {
                self.consume();
                return Ok(string);
            } else {
                string.push(ch);
                self.consume();
            }
        }
        
        Err(format!("Строка не закрыта, строка {} колонка {}", self.line, self.column))
    }

    pub fn read_comment(&mut self) -> Result<String, String> {
        self.consume();
        
        let mut comment = String::new();
        
        if let Some(next_ch) = self.peek() {
            match next_ch {
                '/' => {
                    self.consume();
                    
                    while let Some(ch) = self.peek() {
                        if ch == '\n' {
                            break;
                        } else {
                            comment.push(ch);
                            self.consume();
                        }
                    }
                    Ok(comment)
                },
                '*' => {
                    self.consume();
                    
                    loop {
                        if self.peek() == Some('*') && self.peek_next() == Some('/') {
                            self.consume();
                            self.consume();
                            break;
                        } else if let Some(ch) = self.consume() {
                            comment.push(ch);
                        } else {
                            return Err(format!("Многострочный комментарий не закрыт, строка {}", self.line));
                        }
                    }
                    Ok(comment)
                },
                _ => Err(format!("Неверный символ после '/', строка {} колонка {}", self.line, self.column)),
            }
        } else {
            Err(format!("Неожиданный конец файла после '/', строка {} колонка {}", self.line, self.column))
        }
    }

    pub fn next_token(&mut self) -> Result<Token, String> {
        self.skip_whitespace();
        
        let line = self.line;
        let column = self.column;
        
        if let Some(ch) = self.peek() {
            match ch {
                '{' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::LBrace, line, column })
                },
                '}' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::RBrace, line, column })
                },
                '(' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::LParen, line, column })
                },
                ')' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::RParen, line, column })
                },
                ';' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::Semicolon, line, column })
                },
                '.' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::Dot, line, column })
                },
                ',' => {
                    self.consume();
                    Ok(Token { token_type: TokenType::Comma, line, column })
                },
                '=' => {
                    self.consume();
                    if self.peek() == Some('=') {
                        self.consume();
                        Ok(Token { token_type: TokenType::DoubleEquals, line, column })
                    } else {
                        Ok(Token { token_type: TokenType::Equals, line, column })
                    }
                },
                '"' => {
                    match self.read_string() {
                        Ok(s) => Ok(Token { token_type: TokenType::String(s), line, column }),
                        Err(e) => Err(e),
                    }
                },
                '/' => {
                    match self.read_comment() {
                        Ok(comment) => Ok(Token { token_type: TokenType::Comment(comment), line, column }),
                        Err(e) => Err(e),
                    }
                },
                _ if ch.is_alphabetic() => {
                    let ident = self.read_identifier();
                    match ident.as_str() {
                        "route" => Ok(Token { token_type: TokenType::Route, line, column }),
                        "val" => Ok(Token { token_type: TokenType::Val, line, column }),
                        "if" => Ok(Token { token_type: TokenType::If, line, column }),
                        "else" => Ok(Token { token_type: TokenType::Else, line, column }),
                        "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS" => 
                            Ok(Token { token_type: TokenType::HttpMethod(ident), line, column }),
                        _ => Ok(Token { token_type: TokenType::Identifier(ident), line, column }),
                    }
                },
                _ => Err(format!("Неизвестный символ: '{}', строка {} колонка {}", ch, line, column)),
            }
        } else {
            Ok(Token { token_type: TokenType::EOF, line, column })
        }
    }

    pub fn tokenize(&mut self) -> Result<Vec<Token>, String> {
        let mut tokens = Vec::new();
        
        loop {
            let token = self.next_token()?;
            
            if token.token_type == TokenType::EOF {
                tokens.push(token);
                break;
            }
            
            if let TokenType::Comment(_) = token.token_type {
                continue;
            }
            
            tokens.push(token);
        }
        
        Ok(tokens)
    }
}

#[derive(Debug)]
pub enum AstNode {
    Program(Vec<Box<AstNode>>),
    Route {
        path: String,
        method: String,
        body: Box<AstNode>,
    },
    Block(Vec<Box<AstNode>>),
    VarDeclaration {
        name: String,
        value: Box<AstNode>,
    },
    FunctionCall {
        object: Option<Box<AstNode>>,
        name: String,
        args: Vec<Box<AstNode>>,
    },
    PropertyAccess {
        object: Box<AstNode>,
        property: String,
    },
    IfStatement {
        condition: Box<AstNode>,
        then_branch: Box<AstNode>,
        else_branch: Option<Box<AstNode>>,
    },
    StringLiteral(String),
    Identifier(String),
    BinaryOp {
        left: Box<AstNode>,
        operator: String,
        right: Box<AstNode>,
    },
}

impl fmt::Display for AstNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AstNode::Program(statements) => {
                writeln!(f, "Program:")?;
                for (i, stmt) in statements.iter().enumerate() {
                    write!(f, "  Маршрут {}: {}", i + 1, stmt)?;
                }
                Ok(())
            },
            AstNode::Route { path, method, body } => {
                writeln!(f, "Маршрут: {} {} {}", method, path, body)
            },
            AstNode::Block(statements) => {
                writeln!(f, "{{")?;
                for stmt in statements {
                    write!(f, "    {}", stmt)?;
                }
                write!(f, "}}")
            },
            AstNode::VarDeclaration { name, value } => {
                writeln!(f, "val {} = {};", name, value)
            },
            AstNode::FunctionCall { object, name, args } => {
                if let Some(obj) = object {
                    write!(f, "{}.{}(", obj, name)?;
                } else {
                    write!(f, "{}(", name)?;
                }
                let args_str: Vec<String> = args.iter().map(|arg| format!("{}", arg)).collect();
                write!(f, "{})", args_str.join(", "))
            },
            AstNode::PropertyAccess { object, property } => {
                write!(f, "{}.{}", object, property)
            },
            AstNode::IfStatement { condition, then_branch, else_branch} => {
                write!(f, "if ({}) {}", condition, then_branch)?;
                if let Some(else_stmt) = else_branch {
                    write!(f, " else {}", else_stmt)?;
                }
                writeln!(f)
            },
            AstNode::StringLiteral(value) => write!(f, "\"{}\"", value),
            AstNode::Identifier(name) => write!(f, "{}", name),
            AstNode::BinaryOp { left, operator, right } => {
                write!(f, "{} {} {}", left, operator, right)
            },
        }
    }
}