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:
parent
14bd90d88e
commit
5103ed2971
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
102
src/compiler.rs
102
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<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);
|
||||
}
|
||||
}
|
||||
|
63
src/lexer.rs
63
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<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)
|
||||
}
|
||||
}
|
||||
|
@ -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
4
src/main.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub fn main()
|
||||
{
|
||||
println!("Running draconic");
|
||||
}
|
@ -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
161
src/reader.rs
Normal 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()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user