Compare commits
2 Commits
2830a1befd
...
52bb445f90
Author | SHA1 | Date | |
---|---|---|---|
52bb445f90 | |||
e0a33e22ba |
@ -1,6 +1,7 @@
|
|||||||
mod compile;
|
mod compile;
|
||||||
mod empty;
|
mod empty;
|
||||||
mod help;
|
mod help;
|
||||||
|
mod repl;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
use types::CommandType;
|
use types::CommandType;
|
||||||
|
4
src/cli/repl.rs
Normal file
4
src/cli/repl.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub fn repl_command(_arguments: Vec<String>) {
|
||||||
|
println!("{}", super::get_version());
|
||||||
|
let _ = crate::repl::run();
|
||||||
|
}
|
@ -16,6 +16,7 @@ impl CommandType {
|
|||||||
match self {
|
match self {
|
||||||
CommandType::Help => super::help::help_command(options),
|
CommandType::Help => super::help::help_command(options),
|
||||||
CommandType::Compile => super::compile::compile_command(options),
|
CommandType::Compile => super::compile::compile_command(options),
|
||||||
|
CommandType::Repl => super::repl::repl_command(options),
|
||||||
CommandType::None => super::empty::empty_command(options),
|
CommandType::None => super::empty::empty_command(options),
|
||||||
_ => {
|
_ => {
|
||||||
println!("Not implemented yet! {:?} {:?}", self, options);
|
println!("Not implemented yet! {:?} {:?}", self, options);
|
||||||
|
@ -4,12 +4,14 @@ use std::collections::VecDeque;
|
|||||||
impl PrintableError for LexError {
|
impl PrintableError for LexError {
|
||||||
// TODO: Count and show line number
|
// TODO: Count and show line number
|
||||||
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
||||||
|
let line_number = get_line_number(chars, self.position);
|
||||||
let (erroneous_code, back_count) = get_line(chars, self.position);
|
let (erroneous_code, back_count) = get_line(chars, self.position);
|
||||||
|
|
||||||
let whitespace = vec![' '; back_count].iter().collect::<String>();
|
let whitespace = " ".repeat(back_count + line_number.to_string().len() + 1);
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"\n{}\n{}^\n\n{}{}\n{}",
|
"\n{}|{}\n{}^\n\n{}{}\n{}",
|
||||||
|
line_number,
|
||||||
erroneous_code,
|
erroneous_code,
|
||||||
whitespace,
|
whitespace,
|
||||||
"Invalid character at pos ",
|
"Invalid character at pos ",
|
||||||
@ -69,6 +71,22 @@ fn get_line(chars: &Vec<char>, pos: usize) -> (String, usize) {
|
|||||||
(result_chars.iter().collect::<String>(), pos - before_pos)
|
(result_chars.iter().collect::<String>(), pos - before_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_line_number(chars: &Vec<char>, target_pos: usize) -> usize {
|
||||||
|
let mut count = 1;
|
||||||
|
|
||||||
|
for (pos, char) in chars.iter().enumerate() {
|
||||||
|
if pos >= target_pos {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *char == '\n' {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -85,9 +103,8 @@ mod tests {
|
|||||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||||
let err_str = err_data.get_error_str(&chars);
|
let err_str = err_data.get_error_str(&chars);
|
||||||
|
|
||||||
// TODO: check for line number
|
|
||||||
let expected_str = format!(
|
let expected_str = format!(
|
||||||
"\n{}\n{}^\n\nInvalid character at pos 9\n{}",
|
"\n1|{}\n {}^\n\nInvalid character at pos 9\n{}",
|
||||||
"val name' = 20", " ", "Unrecognized character `'` (escaped: `\\'`)"
|
"val name' = 20", " ", "Unrecognized character `'` (escaped: `\\'`)"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -114,4 +131,22 @@ mod tests {
|
|||||||
assert_eq!("val binding = 322", result);
|
assert_eq!("val binding = 322", result);
|
||||||
assert_eq!(6, back_count);
|
assert_eq!(6, back_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_get_line_number() {
|
||||||
|
let input = String::from("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
|
||||||
|
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||||
|
|
||||||
|
let line_number = get_line_number(&chars, 11);
|
||||||
|
assert_eq!(3, line_number);
|
||||||
|
|
||||||
|
let line_number = get_line_number(&chars, 0);
|
||||||
|
assert_eq!(1, line_number);
|
||||||
|
|
||||||
|
let line_number = get_line_number(&chars, 3);
|
||||||
|
assert_eq!(1, line_number);
|
||||||
|
|
||||||
|
let line_number = get_line_number(&chars, 15);
|
||||||
|
assert_eq!(4, line_number);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use colored::*;
|
||||||
use std::{fs, path::Path};
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
use crate::lexic::token::Token;
|
use crate::lexic::token::Token;
|
||||||
@ -7,28 +8,49 @@ pub fn compile_file(input: &String) {
|
|||||||
let input_path = Path::new(input);
|
let input_path = Path::new(input);
|
||||||
|
|
||||||
if !input_path.is_file() {
|
if !input_path.is_file() {
|
||||||
panic!("Input path is not a valid file")
|
println!(
|
||||||
|
"{}: {} {}",
|
||||||
|
"error".on_red(),
|
||||||
|
"Input path is not a valid file:".red(),
|
||||||
|
input
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = fs::read(input_path).expect("INPUT_PATH should be valid");
|
let bytes = fs::read(input_path).expect("INPUT_PATH should be valid");
|
||||||
let contents = String::from_utf8(bytes).expect("INPUT_PATH's encoding MUST be UTF-8");
|
|
||||||
|
|
||||||
let out_code = compile(&contents);
|
let contents = match String::from_utf8(bytes) {
|
||||||
|
Ok(str) => str,
|
||||||
|
Err(_) => {
|
||||||
|
println!("{}: Input file contains invalid UTF-8", "error".on_red());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(out_code) = compile(&contents) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let mut output_path = Path::new(input).canonicalize().unwrap();
|
let mut output_path = Path::new(input).canonicalize().unwrap();
|
||||||
output_path.set_extension("php");
|
output_path.set_extension("php");
|
||||||
|
|
||||||
fs::write(output_path, out_code).expect("Error writing to output path");
|
fs::write(output_path, out_code).expect("Error writing to output path");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes Lexical analysis, handles errors and calls build_ast for the next phase
|
/// Executes Lexical analysis, handles errors and calls build_ast for the next phase
|
||||||
fn compile(input: &String) -> String {
|
fn compile(input: &String) -> Option<String> {
|
||||||
let tokens = lexic::get_tokens(input);
|
let tokens = lexic::get_tokens(input);
|
||||||
|
|
||||||
match tokens {
|
match tokens {
|
||||||
Ok(tokens) => build_ast(input, tokens),
|
Ok(tokens) => Some(build_ast(input, tokens)),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||||
panic!("{}", error.get_error_str(&chars))
|
println!(
|
||||||
|
"{}:\n{}",
|
||||||
|
"syntax error".on_red(),
|
||||||
|
error.get_error_str(&chars)
|
||||||
|
);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user