diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/aas.rs | 41 | ||||
-rw-r--r-- | src/app.rs | 38 | ||||
-rw-r--r-- | src/app/init.rs | 108 | ||||
-rw-r--r-- | src/app/main.rs | 48 | ||||
-rw-r--r-- | src/app/print_help.rs | 50 | ||||
-rw-r--r-- | src/app/print_version.rs | 35 | ||||
-rw-r--r-- | src/app/run.rs | 56 | ||||
-rw-r--r-- | src/cpu.rs | 52 | ||||
-rw-r--r-- | src/format.rs | 59 | ||||
-rw-r--r-- | src/is_valid_character.rs | 106 | ||||
-rw-r--r-- | src/log.rs | 41 | ||||
-rw-r--r-- | src/token.rs | 36 | ||||
-rw-r--r-- | src/token/tokenise.rs | 131 |
13 files changed, 801 insertions, 0 deletions
diff --git a/src/aas.rs b/src/aas.rs new file mode 100644 index 0000000..33ae452 --- /dev/null +++ b/src/aas.rs @@ -0,0 +1,41 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +extern crate enum_iterator; + +mod app; +mod cpu; +mod format; +mod token; + +mod is_valid_character; +mod log; + +pub use is_valid_character::*; + +pub const VERSION: (u32, u32, u32) = ( + 0x0, // Major + 0x0, // Minor + 0x0, // Patch +); + +fn main() { app::App::main() } diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..caa9379 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,38 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::cpu::Cpu; +use crate::format::Format; + +mod init; +mod main; +mod print_help; +mod print_version; +mod run; + +pub struct App { + input: String, + output: String, + + cpu: Cpu, + format: Format, +} diff --git a/src/app/init.rs b/src/app/init.rs new file mode 100644 index 0000000..8ed43ac --- /dev/null +++ b/src/app/init.rs @@ -0,0 +1,108 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::app::App; +use crate::cpu::Cpu; +use crate::format::Format; + +use std::env::args; +use std::str::FromStr; + +macro_rules! use_remainder { + ($destination: ident, $argument: expr, $index: expr) => {{ + let argument: &[char] = $argument; + let index: usize = $index + 0x1; + + if argument.len() <= index { + let c = argument[index - 0x1]; + Err(format!("missing value for short '{}'", c)) + } else { + let value: String = argument[$index + 0x1..].iter().collect(); + $destination = Some(value); + + break; + } + }}; +} + +impl App { + pub fn init() -> Result<Self, String> { + if args().len() < 0x2 { Self::print_help() }; + + let mut input: Option<String> = None; + let mut output: Option<String> = None; + let mut cpu: Option<String> = None; + let mut format: Option<String> = None; + + let mut handle_short = |argument: &String| -> Result<(), String> { + let argument: Vec<char> = argument.chars().collect(); + + for (index, c) in argument.iter().enumerate().skip(0x1) { + match c { + 'f' => use_remainder!(format, &argument, index)?, + 'h' => Self::print_help(), + 'o' => use_remainder!(output, &argument, index)?, + 'm' => use_remainder!(cpu, &argument, index)?, + 'v' => Self::print_version(), + + _ => { return Err(format!("invalid short parameter '{c}'")) }, + }; + } + + return Ok(()); + }; + + for argument in args().skip(0x1) { + if argument.is_empty() { continue }; + + if argument.chars().nth(0x0) != Some('-') { + // This argument is the input path. + + if input.is_some() { return Err(format!("input pathy is already set (to \"{}\")", input.as_ref().unwrap())) }; + + input = Some(argument.to_owned()); + continue; + } + + handle_short(&argument)?; + } + + // Check if any of the mandatory parameters have + // been set. + if input.is_none() { return Err("missing input path".to_string()) }; + if output.is_none() { output = Some("a.out".to_string()) }; + if cpu.is_none() { return Err("missing cpu".to_string()) }; + + let format = match format { + Some(format) => Format::from_str(&format)?, + _ => Format::default(), + }; + + return Ok(Self { + input: input.unwrap(), + output: output.unwrap(), + + cpu: Cpu::from_str(&cpu.unwrap())?, + format, + }); + } +} diff --git a/src/app/main.rs b/src/app/main.rs new file mode 100644 index 0000000..bd82623 --- /dev/null +++ b/src/app/main.rs @@ -0,0 +1,48 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::log; +use crate::app::App; + +use std::process::exit; + +impl App { + pub fn main() -> ! { + let app = match App::init() { + Ok(app) => app, + + Err(message) => { + log!(error, "{message}"); + exit(0x1); + }, + }; + + exit(match app.run() { + Err(message) => { + log!(error, "{message}"); + 0x1 + }, + + _ => 0x0, + }); + } +} diff --git a/src/app/print_help.rs b/src/app/print_help.rs new file mode 100644 index 0000000..1df895e --- /dev/null +++ b/src/app/print_help.rs @@ -0,0 +1,50 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::app::App; +use crate::cpu::Cpu; +use crate::format::Format; + +use enum_iterator::all; +use std::process::exit; + +impl App { + pub fn print_help() -> ! { + println!("\u{1B}[1mUsage\u{1B}[0m: \u{1B}[1maas\u{1B}[0m \u{1B}[3m[options]\u{1B}[0m <input>"); + println!(); + println!("\u{1B}[1mOptions\u{1B}[0m:"); + println!(" \u{1B}[1m-f\u{1B}[0m\u{1B}[3m<format>\u{1B}[0m set the target executable format (see below)"); + println!(" \u{1B}[1m-h\u{1B}[0m print help"); + println!(" \u{1B}[1m-m\u{1B}[0m\u{1B}[3m<target>\u{1B}[0m set the target cpu (see below)"); + println!(" \u{1B}[1m-v\u{1B}[0m print version"); + + println!(); + println!("\u{1B}[1mCPUs\u{1B}[0m:"); + for cpu in all::<Cpu>() { println!(" \u{1B}[3m{cpu}\u{1B}[0m") } + + println!(); + println!("\u{1B}[1mFormats\u{1B}[0m:"); + for format in all::<Format>() { println!(" \u{1B}[3m{format}\u{1B}[0m") } + + exit(0x0); + } +} diff --git a/src/app/print_version.rs b/src/app/print_version.rs new file mode 100644 index 0000000..d28e457 --- /dev/null +++ b/src/app/print_version.rs @@ -0,0 +1,35 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::VERSION; +use crate::app::App; + +use std::process::exit; + +impl App { + pub fn print_version() -> ! { + println!("\u{1B}[1maas\u{1B}[0m {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2); + println!("\u{1B}[3mCopyright \u{A9} 2023 Gabriel Bj\u{F8}rnager Jensen\u{1B}[0m."); + + exit(0x0); + } +} diff --git a/src/app/run.rs b/src/app/run.rs new file mode 100644 index 0000000..88d0983 --- /dev/null +++ b/src/app/run.rs @@ -0,0 +1,56 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::app::App; +use crate::token::Token; + +use std::fs::read_to_string; + +impl App { + #[must_use] + pub fn run(self) -> Result<(), String> { + if cfg!(debug_assertions) { + println!("\u{1B}[1mSettings\u{1B}[0m:"); + println!("\u{B7} input \u{1B}[3m\"{}\"\u{1B}[0m", self.input); + println!("\u{B7} output \u{1B}[3m\"{}\"\u{1B}[0m", self.output); + println!(); + println!("\u{B7} cpu \u{1B}[3m{}\u{1B}[0m", self.cpu); + println!("\u{B7} format \u{1B}[3m{}\u{1B}[0m", self.format); + println!(); + } + + let input = match read_to_string(&self.input) { + Ok(content) => content, + + _ => return Err(format!("unable to read file \"{}\"", &self.input)), + }; + + let tokens = Token::tokenise(&input)?; + + eprintln!("\u{1B}[1mTokens\u{1B}[0m:"); + for token in &tokens { + eprintln!("\u{B7} {token:?}"); + } + + return Ok(()); + } +} diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..71a82f2 --- /dev/null +++ b/src/cpu.rs @@ -0,0 +1,52 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use enum_iterator::Sequence; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +#[derive(Clone, Copy, Eq, PartialEq, Sequence)] +pub enum Cpu { + Arm7tdmi, +} + +impl Display for Cpu { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + use Cpu::*; + return match *self { + Arm7tdmi => write!(f, "arm7tdmi"), + }; + } +} + +impl FromStr for Cpu { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use Cpu::*; + return match s.to_string().to_lowercase().as_str() { + "arm7tdmi" => Ok(Arm7tdmi), + + _ => Err(format!("invalid target \"{s}\"")), + }; + } +} diff --git a/src/format.rs b/src/format.rs new file mode 100644 index 0000000..b8afcf1 --- /dev/null +++ b/src/format.rs @@ -0,0 +1,59 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use enum_iterator::Sequence; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +#[derive(Clone, Copy, Eq, PartialEq, Sequence)] +pub enum Format { + Elf, +} + +impl Display for Format { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + use Format::*; + return match *self { + Elf => write!(f, "elf"), + }; + } +} + +impl Default for Format { + fn default() -> Self { + use Format::*; + return Elf; + } +} + +impl FromStr for Format { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use Format::*; + return match s.to_string().to_lowercase().as_str() { + "elf" => Ok(Elf), + + _ => Err(format!("invalid format \"{s}\"")), + }; + } +} diff --git a/src/is_valid_character.rs b/src/is_valid_character.rs new file mode 100644 index 0000000..acec7fb --- /dev/null +++ b/src/is_valid_character.rs @@ -0,0 +1,106 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +pub fn is_valid_character(c: char) -> bool { + return match c { + | '\t' + | '\n' + | ' ' + | '!' + | '"' + | '#' + | '*' + | ',' + | '.' + | '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + | ':' + | ';' + | '@' + | 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'G' + | 'H' + | 'I' + | 'J' + | 'K' + | 'L' + | 'M' + | 'N' + | 'O' + | 'P' + | 'Q' + | 'R' + | 'S' + | 'T' + | 'U' + | 'V' + | 'W' + | 'X' + | 'Y' + | 'Z' + | '[' + | ']' + | '_' + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | 'g' + | 'h' + | 'i' + | 'j' + | 'k' + | 'l' + | 'm' + | 'n' + | 'o' + | 'p' + | 'q' + | 'r' + | 's' + | 't' + | 'u' + | 'v' + | 'w' + | 'x' + | 'y' + | 'z' + => true, + + _ => false, + }; +} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..99601b3 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,41 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +#[macro_export] +macro_rules! log { + (error, $($message: tt)*) => {{ + eprintln!("\u{1B}[1m\u{1B}[91merror\u{1B}[0m: {}", format!($($message)?)); + }}; + + (note, $($message: tt)*) => {{q + eprintln!("\u{1B}[1m\u{1B}[95mnote\u{1B}[0m: {}", format!($($message)?)); + }}; + + (status, $($message: tt)*) => {{ + eprintln!("{}", format!($($message)?)); + }}; + + (warning, $($message: tt)*) => {{ + use crate::log::log; + eprintln!("\u{1B}[1m\u{1B}[93mwarning\u{1B}[0m: {}", format!($($message)?)); + }}; +} diff --git a/src/token.rs b/src/token.rs new file mode 100644 index 0000000..68b7f4e --- /dev/null +++ b/src/token.rs @@ -0,0 +1,36 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +mod tokenise; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Token { + BracketLeft, + BracketRight, + Colon, + Comma, + Fullstop, + Hashtag, + Return, + StringLiteral(String), + Word(String), +} diff --git a/src/token/tokenise.rs b/src/token/tokenise.rs new file mode 100644 index 0000000..44ce683 --- /dev/null +++ b/src/token/tokenise.rs @@ -0,0 +1,131 @@ +/* + Copyright 2023 Gabriel Jensen. + + This file is part of aas. + + aas is free software: you can redistribute it + and/or modify it under the terms of the GNU + General Public License as published by the Free + Software Foundation, either version 3 of the + License, or (at your option) any later version. + + aas is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU + General Public License along with aas. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::is_valid_character; +use crate::token::Token; + +impl Token { + #[must_use] + pub fn tokenise(input: &str) -> Result<Vec<Self>, String> { + let mut tokens: Vec<Self> = Vec::new(); + + let mut input_index: usize = 0x0; + while let Some(token) = get_next_token(&input, &mut input_index)? { tokens.push(token) } + + return Ok(tokens); + } +} + +#[must_use] +fn get_next_token(input: &str, index: &mut usize) -> Result<Option<Token>, String> { + use Token::*; + + let mut word = String::new(); + + let mut in_comment = false; + let mut in_string = false; + + for c in input.chars().skip(*index) { + // Skip until we're out of the comment. + if in_comment { + if c != '\n' { + *index += 0x1; + continue; + } + + in_comment = false; + } + + // Finish the string (if inside one) and return. + if in_string { + *index += 0x1; + + if c != '"' { + word.push(c); + continue; + } + + return Ok(Some(StringLiteral(word))); + } + + // We don't care about invalid character inside of + // comments or strings. + if !is_valid_character(c) { return Err(format!("invalid character U+{:04X} '{c}' ({index} / {})", c as u32, input.len())) }; + + // Check if the word is terminated. If it was, we + // don't count this character. + if !word.is_empty() { + match c { + | ' ' + | '\t' + | '\n' + | '.' + | ',' + | ':' + | ';' + | '@' + => return Ok(Some(Word(word))), + + _ => {}, + }; + } + + // There aren't any more things to complete + // (comments, strings, or words), so we know now + // that no more characters will be skipped. + *index += 0x1; + + match c { + | ' ' + | '\t' + => continue, + + '\n' => return Ok(Some(Return)), + '[' => return Ok(Some(BracketLeft)), + ']' => return Ok(Some(BracketRight)), + '.' => return Ok(Some(Fullstop)), + ',' => return Ok(Some(Comma)), + ':' => return Ok(Some(Colon)), + '#' => return Ok(Some(Hashtag)), + + | ';' + | '@' + => { + in_comment = true; + continue; + }, + + '"' => { + in_string = true; + continue; + } + + _ => {}, + }; + + word.push(c); + } + + if in_string { return Err("unterminated string".to_string()) }; + + return Ok(None); +} |