diff --git a/Cargo.lock b/Cargo.lock index 18ad8da..e00d68d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,12 @@ [root] name = "draconic" version = "0.1.0" +dependencies = [ + "scribe 0.1.0 (git+https://gitlab.com/CyberMages/scribe.git)", +] + +[[package]] +name = "scribe" +version = "0.1.0" +source = "git+https://gitlab.com/CyberMages/scribe.git#c655eca358577795d818bdd07cb71864c0c9b9f2" diff --git a/Cargo.toml b/Cargo.toml index a8fda4f..7350f86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,6 @@ repository = "https://gitlab.com/CyberMages/draconic.git" documentation = "" keywords = ["draconic", "parser", "interpreter", "compiler"] + +[dependencies.scribe] +git = "https://gitlab.com/CyberMages/scribe.git" diff --git a/examples/parse_test.rs b/examples/compile.rs similarity index 86% rename from examples/parse_test.rs rename to examples/compile.rs index 6585eb1..be659a4 100644 --- a/examples/parse_test.rs +++ b/examples/compile.rs @@ -4,6 +4,8 @@ extern crate draconic; use std::path::PathBuf; +use draconic::Compiler; + /// @@ -13,7 +15,7 @@ pub const RESOURCE_DIR: &'static str = "resources"; pub const EXAMPLES_DIR: &'static str = "examples"; /// -pub const TEST_INPUT_FILENAME: &'static str = "test.tpl"; +pub const TEST_INPUT_FILENAME: &'static str = "test.drs"; /// pub const TEST_OUTPUT_FILENAME: &'static str = "test.rs"; @@ -39,4 +41,5 @@ pub fn main() output.push(TEST_OUTPUT_FILENAME); println!("Compiling {:?} to {:?}", input, output); + Compiler::compile(input, output); } diff --git a/resources/test.pdt b/resources/test.drs similarity index 100% rename from resources/test.pdt rename to resources/test.drs diff --git a/src/compiler.rs b/src/compiler.rs index 288232e..d8a6447 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,3 +1,105 @@ +use std::fs::File; +use std::io::Write; +use std::path::Path; + +use super::lexer::Lexer; +use super::reader::Reader; + + +const FILE_HEADER: &'static str = + "// This is a generated file from the Draconic compiler.\n\ + //\n\ + // Do NOT edit this file.\n"; + + + +/// pub enum Compiler { } + + + +/// Reads an input File and parses it. +fn read_file(input_path: &Path) -> String +{ + let mut output: String; + let mut reader: Reader; + + output = String::from(FILE_HEADER); + + // Create a Reader from the given input file. + reader = Reader::from_file(input_path); + + // Use the Lexer to scan the Reader's + // buffer into tokens. + match Lexer::scan(reader) + { + Ok(tokens) => + { + for token in tokens + { + output.push_str(&token); + } + } + + Err(error) => + { + error!("{}", error); + } + } + + output +} + +/// Writes an output File. +fn write_file(output_path: &Path, output: String) +{ + // Delete the output file if it already exists. + if output_path.exists() == true + { + match ::std::fs::remove_file(output_path) + { + Ok(_) => + { + } + + Err(error) => + { + warn!("{}", error); + } + } + } + + // Write the compiled String to a File. + match File::create(output_path) + { + Ok(mut file) => + { + file.write_all(output.as_bytes()); + } + + Err(error) => + { + error!("{}", error); + } + } +} + + + +impl Compiler +{ + /// Compile + pub fn compile(input: F, output: F) + where F: AsRef + { + let output_string: String; + + // Turn the input file into a compiled String. + output_string = read_file(input.as_ref()); + + // Write the compiled output file. + write_file(output.as_ref(), output_string); + } +} diff --git a/src/lexer.rs b/src/lexer.rs index c8c721e..b8f3a51 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,3 +1,66 @@ +use super::reader::Reader; + + + +/// pub enum Lexer { } + + +/// Determines if a character is alphabetic. +fn is_alpha(c: char) -> bool +{ + c.is_alphabetic() +} + +/// Determines if a character is a numeral +fn is_numeric(c: char) -> bool +{ + c.is_numeric() +} + +/// Determines if a character is whitespace. +fn is_whitespace(c: char) -> bool +{ + c.is_whitespace() +} + +/// Returns true if the next set of characters starts with +/// the given pattern; Otherwise, false. +fn starts_with

(pattern: P) -> bool + where P: AsRef +{ + false +} + +/// Consume and discard zero or more whitespace characters. +fn consume_whitespace() +{ + consume_while(is_whitespace); +} + +/// Consume characters until the test returns false. +fn consume_while(test: F) -> String + where F: Fn(char) -> bool +{ + String::new() +} + + + +impl Lexer +{ + /// Scan the data held by the Reader. This will consume the Reader and + /// clear it. + pub fn scan(reader: Reader) -> Result, String> + { + let tokens: Vec; + + // Create a new list to hold all the discovered tokens. + tokens = Vec::new(); + + // Return the scanned tokens. + Ok(tokens) + } +} diff --git a/src/lib.rs b/src/lib.rs index 879e088..8dbe9bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,13 @@ +#[macro_use] +extern crate scribe; + + + mod compiler; mod lexer; mod parser; +mod reader; pub use self::compiler::Compiler; -pub use self::lexer::Lexer; -pub use self::parser::Parser; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7238664 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,4 @@ +pub fn main() +{ + println!("Running draconic"); +} diff --git a/src/parser.rs b/src/parser.rs index 02d814f..febe672 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -17,12 +17,6 @@ impl Parser { } - /// Get, but don't consume, the current character. - pub fn get_char(&self) -> char - { - 'a' - } - /// Returns true if the next set of characters starts with /// the given pattern; Otherwise, false. pub fn starts_with

(&self, pattern: P) -> bool @@ -30,36 +24,4 @@ impl Parser { self.position >= self.input.len() } - - /// Returns true if all the input has been consumed; - /// Otherwise, false. - pub fn end_of_file(&self) -> bool - { - self.position >= self.input.len() - } - - /// Return the current character and advance to the next one. - pub fn consume_char(&mut self) -> char - { - 'a' - } - - /// Consume and discard zero or more whitespace characters. - pub fn consume_whitespace(&mut self) - { - self.consume_while(Parser::is_whitespace); - } - - /// Consume characters until the test returns false. - pub fn consume_while(&mut self, test: F) -> String - where F: Fn(char) -> bool - { - String::new() - } - - /// Determines if a character is whitespace. - fn is_whitespace(c: char) -> bool - { - c.is_whitespace() - } } diff --git a/src/reader.rs b/src/reader.rs new file mode 100644 index 0000000..504d0b0 --- /dev/null +++ b/src/reader.rs @@ -0,0 +1,161 @@ +use std::fs::File; +use std::io::Read; +use std::path::Path; + + + +// TODO: Make this work on only a predetermined size of a buffer. + + +/// Stores some input and allows it to be +/// read on a character by character basis. +/// +pub struct Reader +{ + /// The current position in the buffer. + position: usize, + + /// The buffer of the input data to be read. + buffer: Vec +} + + + +impl Reader +{ + /// Create a new empty Reader. + pub fn new() -> Reader + { + Reader + { + position: 0usize, + buffer: Vec::new(), + } + } + + /// Create a new reader from a File. + pub fn from_file(filepath: F) -> Reader + where F: AsRef + { + let mut reader: Reader; + + // Create a new Reader and load the data + // for it from the file at the given filepath. + reader = Reader::new(); + reader.load_from_file(filepath); + + reader + } + + /// Create a new reader from a String. + pub fn from_string(data: String) -> Reader + { + let mut reader: Reader; + + // Create a new Reader and load the data to it. + reader = Reader::new(); + reader.load_from_string(data); + + reader + } + + /// Reset the Reader back to the starting position. + pub fn reset(&mut self) + { + // Move back to the start of the buffer. + self.position = 0usize; + } + + /// Clear the Reader of all data. + pub fn clear(&mut self) + { + self.buffer = Vec::new(); + self.reset(); + } + + /// Clears the Reader and sets it to read from a File. + pub fn load_from_file(&mut self, filepath: F) + where F: AsRef + { + let mut data: String; + + // Open the file for reading and load the + // data into a String that will be parsed. + data = String::new(); + match File::open(filepath.as_ref()) + { + Ok(mut file) => + { + match file.read_to_string(&mut data) + { + Ok(bytes_read) => + { + info!("{} bytes read from {:?}", + bytes_read, filepath.as_ref()); + } + + Err(error) => + { + error!("{}", error); + } + } + } + + Err(error) => + { + error!("{}", error); + } + } + + // Load the String read from the file. + self.load_from_string(data); + } + + /// Clears the Reader and sets it to read from the given data. + pub fn load_from_string(&mut self, data: String) + { + // Clear the Reader. + self.clear(); + + // Parse the String that the reader is + // reading from as a set of characters + // in the Reader's buffer. + for character in data.chars() + { + self.buffer.push(character); + } + } + + /// Get the current character. + pub fn get_char(&self) -> Result + { + if self.is_eof() == false + { + Ok(self.buffer[self.position]) + } + else + { + Err("Unable to read character. \ + No characters left in the input buffer.") + } + } + + /// Get the current character and move the + /// Reader on to the next character. + pub fn consume_char(&mut self) -> Result + { + let character: char; + + character = try!(self.get_char()); + + self.position += 1; + + Ok(character) + } + + /// + fn is_eof(&self) -> bool + { + self.position >= self.buffer.len() + } +}