The Draconian compiler can now read a file.

A file can be read and placed into a Reader buffer for lexicographical
analysis. This buffer can be consumed on a character by character basis.
This commit is contained in:
Myrddin Dundragon 2016-07-16 17:07:47 -04:00
parent 14bd90d88e
commit 5103ed2971
10 changed files with 351 additions and 41 deletions

8
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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);
}

View File

@ -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<F>(input: F, output: F)
where F: AsRef<Path>
{
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);
}
}

View File

@ -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<P>(pattern: P) -> bool
where P: AsRef<str>
{
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<F>(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<Vec<String>, String>
{
let tokens: Vec<String>;
// Create a new list to hold all the discovered tokens.
tokens = Vec::new();
// Return the scanned tokens.
Ok(tokens)
}
}

View File

@ -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;

4
src/main.rs Normal file
View File

@ -0,0 +1,4 @@
pub fn main()
{
println!("Running draconic");
}

View File

@ -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<P>(&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<F>(&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()
}
}

161
src/reader.rs Normal file
View File

@ -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<char>
}
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<F>(filepath: F) -> Reader
where F: AsRef<Path>
{
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<F>(&mut self, filepath: F)
where F: AsRef<Path>
{
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<char, &'static str>
{
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<char, &'static str>
{
let character: char;
character = try!(self.get_char());
self.position += 1;
Ok(character)
}
///
fn is_eof(&self) -> bool
{
self.position >= self.buffer.len()
}
}