Skip to content

Commit

Permalink
Begin implementing parser...
Browse files Browse the repository at this point in the history
  • Loading branch information
WilliamRagstad committed Aug 10, 2023
1 parent 943718a commit 7bc791d
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 13 deletions.
162 changes: 151 additions & 11 deletions src/file/parser.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,155 @@
use std::{path::PathBuf, io::BufReader};
use crate::file::webx::WebXFile;
use std::{path::PathBuf, io::{BufReader, Read, Seek, SeekFrom}};
use crate::{file::webx::WebXFile, reporting::error::{exit_error, ERROR_PARSE_IO, ERROR_SYNTAX}};

pub fn parse_webx_file(file: &PathBuf) -> Result<WebXFile, String> {
let module = WebXFile {
path: file.clone(),
includes: vec![],
scopes: vec![],
};
let file_contents = std::fs::read_to_string(file).map_err(|e| e.to_string())?;
struct WebXFileParser<'a> {
file: &'a PathBuf,
content: &'a String,
reader: BufReader<&'static [u8]>,
line: usize,
column: usize,
index: u64,
}

impl<'a> WebXFileParser<'a> {
fn new(file: &'a PathBuf, content: &'a String) -> WebXFileParser<'a> {
WebXFileParser {
file,
content,
reader: BufReader::new(content.as_bytes()),
line: 0,
column: 0,
index: 0,
}
}

/// Returns the next character in the file, or None if EOF is reached.
/// Increments the line and column counters.
///
/// # Errors
/// If the file cannot be read, an error is returned and the program exits.
fn next(&mut self) -> Option<char> {
let mut buf = [0; 1];
let bytes_read = match self.reader.read(&mut buf) {
Ok(n) => n,
Err(e) => exit_error(format!("Failed to read file '{}' due to, {}", self.file.display(), e), ERROR_PARSE_IO),
};
if bytes_read == 0 { return None; }
let c = buf[0] as char;
self.index += 1;
if c == '\n' {
self.line += 1;
self.column = 0;
} else {
self.column += 1;
}
Some(c)
}

/// Expect a specific character to be next in the file.
/// Increments the line and column counters.
///
/// # Errors
/// If EOF is reached, or the next character is not the expected one, an error is returned and the program exits.
fn expect_specific(&mut self, c: char) {
let nc = self.next();
if nc.is_none() {
exit_error(format!("Unexpected EOF, expected '{}' at line {}, column {}", c, self.line, self.column), ERROR_SYNTAX);
} else if nc.unwrap() != c {
exit_error(format!("Expected '{}' but found '{}' at line {}, column {}", c, nc.unwrap(), self.line, self.column), ERROR_SYNTAX);
}
}

fn expect_specific_str(&mut self, s: &str) {
for c in s.chars() {
self.expect_specific(c);
}
}

/// Expect any of the given characters to be next in the file.
/// Increments the line and column counters.
///
/// # Errors
/// If EOF is reached, or the next character is not one of the expected ones, an error is returned and the program exits.
fn expect_any_of(&mut self, cs: Vec<char>) -> char {
let nc = self.next();
if nc.is_none() {
exit_error(format!("Unexpected EOF, expected any of {:?} at line {}, column {}", cs, self.line, self.column), ERROR_SYNTAX);
}
let nc = nc.unwrap();
if !cs.contains(&nc) {
exit_error(format!("Expected any of {:?} but found '{}' at line {}, column {}", cs, nc.unwrap(), self.line, self.column), ERROR_SYNTAX);
}
nc
}

fn parse_comment(&mut self) {
match self.expect_any_of(vec!['/', '*']) {
'/' => {
loop {
let c = self.next();
if c.is_none() { break; }
if c.unwrap() == '\n' { break; }
}
},
'*' => {
loop {
let c = self.next();
if c.is_none() { break; }
if c.unwrap() == '*' {
let c = self.next();
if c.is_none() { break; }
if c.unwrap() == '/' { break; }
}
}
},
}
}

let reader = BufReader::new(file_contents.as_bytes());
fn parse_location_scope(&mut self) {
self.expect_specific_str("ocation");
loop {
let c = self.next();
if c.is_none() { break; }
if c.unwrap() == '}' { break; }
}
}

Ok(module)
fn parse_scope(&mut self) {

}

fn parse_module(&mut self) -> Result<WebXFile, String> {
let mut module = WebXFile {
path: self.file.clone(),
includes: vec![],
scopes: vec![],
};

// Keywords: handler, include, location, module, { } and all HTTP methods.
// Only expect a keyword at the start of a line, whitespace, or // comments.
// Pass to dedicated parser function, otherwise error.

loop {
let c = self.next();
if c.is_none() { break; }
let c = c.unwrap();
match c {
' ' | '\t' | '\n' => (), // Ignore whitespace.
'/' => self.parse_comment(),
'{' => self.parse_location_scope(),
'i' => self.parse_include(),
'l' => self.parse_location(),
'm' => self.parse_module_keyword(),
'h' => self.parse_handler(),
}
}

Ok(module)
}
}

pub fn parse_webx_file(file: &PathBuf) -> Result<WebXFile, String> {
let file_contents = std::fs::read_to_string(file).map_err(|e| e.to_string())?;
let mut parser = WebXFileParser::new(file, &file_contents);
Ok(parser.parse_module()?)
}
6 changes: 4 additions & 2 deletions src/reporting/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use colored::*;
pub static ERROR_READ_WEBX_FILES: i32 = 1;
pub static ERROR_READ_PROJECT_CONFIG: i32 = 2;
pub static ERROR_CIRCULAR_DEPENDENCY: i32 = 3;
pub static ERROR_PARSE_IO: i32 = 4;
pub static ERROR_SYNTAX: i32 = 5;

fn error_generic(message: String, error_name: &str) {
eprintln!("{} {}", error_name.red(), message);
eprintln!("{}: {}", error_name.red(), message);
}

fn exit_error_generic(message: String, code: i32, error_name: &str) -> ! {
error_generic(message, error_name);
error_generic(message, format!("{} ({})", error_name, code).as_str());
std::process::exit(code);
}

Expand Down

0 comments on commit 7bc791d

Please sign in to comment.