summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.rs20
-rw-r--r--src/app/init.rs108
-rw-r--r--src/app/main.rs32
-rw-r--r--src/app/new.rs37
-rw-r--r--src/app/print_help.rs33
-rw-r--r--src/app/print_version.rs12
-rw-r--r--src/app/run.rs41
-rw-r--r--src/configuration.rs36
-rw-r--r--src/configuration/from_arguments.rs153
-rw-r--r--src/eas.rs (renamed from src/aas.rs)20
-rw-r--r--src/encode_state.rs36
-rw-r--r--src/encoding.rs62
-rw-r--r--src/error.rs123
-rw-r--r--src/format.rs20
-rw-r--r--src/is_valid_character.rs25
-rw-r--r--src/log.rs25
-rw-r--r--src/node.rs33
-rw-r--r--src/node/encode.rs56
-rw-r--r--src/node/parse.rs90
-rw-r--r--src/processor.rs (renamed from src/cpu.rs)30
-rw-r--r--src/source_location.rs58
-rw-r--r--src/token.rs13
-rw-r--r--src/token/tokenise.rs185
23 files changed, 946 insertions, 302 deletions
diff --git a/src/app.rs b/src/app.rs
index f9722ee..dce98c0 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,30 +1,31 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
-use crate::cpu::Cpu;
+use crate::processor::Processor;
+use crate::encoding::Encoding;
use crate::format::Format;
-mod init;
mod main;
+mod new;
mod print_help;
mod print_version;
mod run;
@@ -33,6 +34,7 @@ pub struct App {
input: String,
output: String,
- cpu: Cpu,
- format: Format,
+ encoding: Encoding,
+ processor: Processor,
+ format: Format,
}
diff --git a/src/app/init.rs b/src/app/init.rs
deleted file mode 100644
index 3375d09..0000000
--- a/src/app/init.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- Copyright 2023 Gabriel Bjørnager 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
index bc99952..828abc8 100644
--- a/src/app/main.rs
+++ b/src/app/main.rs
@@ -1,48 +1,52 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
use crate::log;
use crate::app::App;
+use crate::configuration::Configuration;
use std::process::exit;
impl App {
pub fn main() -> ! {
- let app = match App::init() {
+ let configuration = match Configuration::from_arguments() {
Ok(app) => app,
- Err(message) => {
- log!(error, "{message}");
+ Err(error) => {
+ log!(error, error);
exit(0x1);
},
};
- exit(match app.run() {
- Err(message) => {
- log!(error, "{message}");
- 0x1
+ let app = App::new(configuration);
+ match app.run() {
+ Err(error) => {
+ log!(error, error);
+ exit(0x1);
},
- _ => 0x0,
- });
+ _ => {},
+ };
+
+ exit(0x0);
}
}
diff --git a/src/app/new.rs b/src/app/new.rs
new file mode 100644
index 0000000..d0325f8
--- /dev/null
+++ b/src/app/new.rs
@@ -0,0 +1,37 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::app::App;
+use crate::configuration::Configuration;
+
+impl App {
+ pub fn new(configuration: Configuration) -> Self {
+ return Self {
+ input: configuration.input,
+ output: configuration.output,
+
+ encoding: configuration.encoding,
+ processor: configuration.processor,
+ format: configuration.format,
+ };
+ }
+}
diff --git a/src/app/print_help.rs b/src/app/print_help.rs
index 0499ee1..41006da 100644
--- a/src/app/print_help.rs
+++ b/src/app/print_help.rs
@@ -1,45 +1,52 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
use crate::app::App;
-use crate::cpu::Cpu;
+use crate::encoding::Encoding;
use crate::format::Format;
+use crate::processor::Processor;
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!("\u{1B}[1mUsage\u{1B}[0m: eas {{<options>}} <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!(" \u{1B}[1m-c\u{1B}[0m\u{1B}[3m<encoding>\u{1B}[23m sets the expected input file encoding (character set) to \u{1B}[3mencoding\u{1B}[23m");
+ println!(" \u{1B}[1m-f\u{1B}[0m\u{1B}[3m<format>\u{1B}[23m sets the target executable format to \u{1B}[3mformat\u{1B}[23m");
+ println!(" \u{1B}[1m-m\u{1B}[0m\u{1B}[3m<processor>\u{1B}[23m sets the target processor (machine) to \u{1B}[3mmachine\u{1B}[23m");
+ println!();
+ println!(" \u{1B}[1m--help\u{1B}[0m prints help");
+ println!(" \u{1B}[1m--version\u{1B}[0m prints versioning");
+
+ println!();
+ println!("\u{1B}[1mEncodings\u{1B}[0m:");
+ for encoding in all::<Encoding>() { println!(" \u{1B}[3m{encoding}\u{1B}[0m") }
println!();
- println!("\u{1B}[1mCPUs\u{1B}[0m:");
- for cpu in all::<Cpu>() { println!(" \u{1B}[3m{cpu}\u{1B}[0m") }
+ println!("\u{1B}[1mProcessors\u{1B}[0m:");
+ for processor in all::<Processor>() { println!(" \u{1B}[3m{processor}\u{1B}[0m") }
println!();
println!("\u{1B}[1mFormats\u{1B}[0m:");
diff --git a/src/app/print_version.rs b/src/app/print_version.rs
index 73b0743..1f207ca 100644
--- a/src/app/print_version.rs
+++ b/src/app/print_version.rs
@@ -1,22 +1,22 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
@@ -27,7 +27,7 @@ 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}[1meAS\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
index f2ef4a4..5e0d838 100644
--- a/src/app/run.rs
+++ b/src/app/run.rs
@@ -1,54 +1,69 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
use crate::app::App;
+use crate::error::Error;
+use crate::node::Node;
+use crate::source_location::SourceLocation;
use crate::token::Token;
use std::fs::read_to_string;
impl App {
#[must_use]
- pub fn run(self) -> Result<(), String> {
+ pub fn run(self) -> Result<(), Error> {
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!("\u{B7} encoding \u{1B}[3m{}\u{1B}[0m", self.encoding);
+ println!("\u{B7} processor \u{1B}[3m{}\u{1B}[0m", self.processor);
+ 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)),
+ _ => return Err(Error::AccessDenied(self.input.clone())),
};
- let tokens = Token::tokenise(&input)?;
+ let tokens = Token::tokenise(&input, &mut SourceLocation::new(&self.input))?;
- eprintln!("\u{1B}[1mTokens\u{1B}[0m:");
- for token in &tokens {
- eprintln!("\u{B7} {token:?}");
+ if cfg!(debug_assertions) {
+ eprintln!("\u{1B}[1mTokens\u{1B}[0m:");
+ for (location, token) in &tokens {
+ eprintln!("\u{B7} {location}: {token:?}");
+ }
+ }
+
+ let nodes = Node::parse(&tokens)?;
+
+ if cfg!(debug_assertions) {
+ eprintln!("\u{1B}[1mNodes\u{1B}[0m:");
+ for node in &nodes {
+ eprintln!("\u{B7} {node:?}");
+ }
}
return Ok(());
diff --git a/src/configuration.rs b/src/configuration.rs
new file mode 100644
index 0000000..cff1e46
--- /dev/null
+++ b/src/configuration.rs
@@ -0,0 +1,36 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::encoding::Encoding;
+use crate::format::Format;
+use crate::processor::Processor;
+
+mod from_arguments;
+
+pub struct Configuration {
+ pub input: String,
+ pub output: String,
+
+ pub encoding: Encoding,
+ pub processor: Processor,
+ pub format: Format,
+}
diff --git a/src/configuration/from_arguments.rs b/src/configuration/from_arguments.rs
new file mode 100644
index 0000000..a62813c
--- /dev/null
+++ b/src/configuration/from_arguments.rs
@@ -0,0 +1,153 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::app::App;
+use crate::configuration::Configuration;
+use crate::encoding::Encoding;
+use crate::error::Error;
+use crate::processor::Processor;
+use crate::format::Format;
+
+use std::env::args;
+use std::str::FromStr;
+
+struct ConfigurationTemporary {
+ pub input: Option<String>,
+ pub output: Option<String>,
+
+ pub encoding: Option<String>,
+ pub processor: Option<String>,
+ pub format: Option<String>,
+}
+
+impl Configuration {
+ pub fn from_arguments() -> Result<Self, Error> {
+ if args().len() < 0x2 { return Err(Error::MissingInputFile) };
+
+ let mut temporary = ConfigurationTemporary {
+ input: None,
+ output: None,
+
+ encoding: None,
+ processor: None,
+ format: None,
+ };
+
+ for argument in args().skip(0x1) {
+ // Skip the first, we assume that it's just the
+ // executable invoked.
+
+ if argument.is_empty() { continue };
+ let argument: Vec<char> = argument.chars().collect();
+
+ if argument[0x0] != '-' {
+ // This argument is the input path.
+
+ temporary.input = Some(argument.into_iter().collect());
+ continue;
+ }
+
+ match argument.get(0x1) {
+ Some('-') => handle_long_argument( &mut temporary, argument.into_iter().skip(0x2).collect())?,
+ Some(_) => handle_short_argument(&mut temporary, &argument[0x1..])?,
+
+ _ => return Err(Error::MissingShortArgument),
+ }
+ }
+
+ // Check if any of the mandatory parameters have
+ // been set.
+ if temporary.input.is_none() { return Err(Error::MissingInputFile) };
+ if temporary.processor.is_none() { return Err(Error::MissingTargetProcessor) };
+
+ // The output path defaults to "a.out".
+ // TODO: Do something better.
+ if temporary.output.is_none() { temporary.output = Some(String::from("a.out")) };
+
+ let processor = Processor::from_str(&temporary.processor.unwrap())?;
+
+ let encoding = match temporary.encoding {
+ Some(format) => Encoding::from_str(&format)?,
+ _ => Encoding::default(),
+ };
+
+ let format = match temporary.format {
+ Some(format) => Format::from_str(&format)?,
+ _ => Format::default(),
+ };
+
+ return Ok(Self {
+ input: temporary.input.unwrap(),
+ output: temporary.output.unwrap(),
+
+ encoding,
+ processor,
+ format,
+ });
+ }
+}
+
+fn handle_short_argument(temporary: &mut ConfigurationTemporary, argument: &[char]) -> Result<(), Error> {
+ macro_rules! use_remainder_as_value {
+ ($argument: expr, $index: expr) => {{
+ let argument: &[char] = $argument;
+ let index: usize = $index;
+
+ let next_index = index + 0x1;
+
+ if argument.len() <= next_index {
+ // There are no more characters, and thus no values.
+ let c = argument[index];
+ Err(Error::MissingShortValue(c))
+ } else {
+ let value: String = argument[next_index..].iter().collect();
+ Ok(value)
+ }
+ }};
+ }
+
+ for (index, c) in argument.iter().enumerate() {
+ match c {
+ 'c' => { temporary.encoding = Some(use_remainder_as_value!(&argument, index)?); break; },
+ 'f' => { temporary.format = Some(use_remainder_as_value!(&argument, index)?); break; },
+ 'm' => { temporary.processor = Some(use_remainder_as_value!(&argument, index)?); break; },
+ 'o' => { temporary.output = Some(use_remainder_as_value!(&argument, index)?); break; },
+
+ 'h' => App::print_help(),
+
+ _ => return Err(Error::InvalidShortParameter(*c)),
+ };
+ }
+
+ return Ok(());
+}
+
+fn handle_long_argument(_temporary: &mut ConfigurationTemporary, argument: String) -> Result<(), Error> {
+ match argument.as_str() {
+ "help" => App::print_help(),
+ "version" => App::print_version(),
+
+ _ => return Err(Error::InvalidLongParameter(argument.to_string())),
+ };
+
+ return Ok(());
+}
diff --git a/src/aas.rs b/src/eas.rs
index 78302eb..729580a 100644
--- a/src/aas.rs
+++ b/src/eas.rs
@@ -1,30 +1,36 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
extern crate enum_iterator;
mod app;
-mod cpu;
+mod configuration;
+mod encode_state;
+mod encoding;
+mod error;
mod format;
+mod node;
+mod processor;
+mod source_location;
mod token;
mod is_valid_character;
@@ -34,7 +40,7 @@ pub use is_valid_character::*;
pub const VERSION: (u32, u32, u32) = (
0x0, // Major
- 0x2, // Minor
+ 0x3, // Minor
0x0, // Patch
);
diff --git a/src/encode_state.rs b/src/encode_state.rs
new file mode 100644
index 0000000..42d45f0
--- /dev/null
+++ b/src/encode_state.rs
@@ -0,0 +1,36 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+#[derive(Clone, Debug)]
+pub struct EncodeState {
+ pub code16: bool,
+}
+
+impl EncodeState {
+ pub const fn new() -> Self { EncodeState {
+ code16: false,
+ } }
+}
+
+impl Default for EncodeState {
+ fn default() -> Self { EncodeState::new() }
+}
diff --git a/src/encoding.rs b/src/encoding.rs
new file mode 100644
index 0000000..70f9be8
--- /dev/null
+++ b/src/encoding.rs
@@ -0,0 +1,62 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::error::Error;
+
+use enum_iterator::Sequence;
+use std::fmt::{Display, Formatter};
+use std::str::FromStr;
+
+#[derive(Clone, Copy, Eq, PartialEq, Sequence)]
+pub enum Encoding {
+ Utf8,
+}
+
+impl Display for Encoding {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ use Encoding::*;
+ return match *self {
+ Utf8 => write!(f, "utf8"),
+ };
+ }
+}
+
+impl Default for Encoding {
+ fn default() -> Self {
+ return Encoding::Utf8;
+ }
+}
+
+impl FromStr for Encoding {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use Encoding::*;
+
+ let s = s.to_string().to_lowercase();
+ return match s.as_str() {
+ "utf8" => Ok(Utf8),
+
+ _ => Err(Error::InvalidFileEncoding(s)),
+ };
+ }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..fcb2db0
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,123 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::source_location::SourceLocation;
+
+// e0??? | Syntax errors
+// e1??? | CLI errors
+// e2??? | Internal errors
+
+#[derive(Clone)]
+pub enum Error {
+ EndOfFile( String),
+ UnknownMnemonic( String, SourceLocation),
+ AccessDenied( String),
+ IllegalCharacter( char, SourceLocation),
+ IncompleteNode( SourceLocation),
+ InvalidShortParameter( char),
+ MissingInputFile,
+ MissingTargetProcessor,
+ UnterminatedString( SourceLocation),
+ MissingShortValue( char),
+ InvalidTargetFormat( String),
+ InvalidTargetProcessor(String),
+ InvalidLongParameter( String),
+ MissingLongValue( String),
+ MissingShortArgument,
+ InvalidFileEncoding( String),
+ MissingLongArgument,
+}
+
+impl Error {
+ #[must_use]
+ pub const fn code(&self) -> u16 {
+ use Error::*;
+
+ return match *self {
+ EndOfFile( ..) => 0x0000,
+ UnknownMnemonic( ..) => 0x0001,
+ IllegalCharacter( ..) => 0x0002,
+ IncompleteNode( ..) => 0x0003,
+ UnterminatedString( ..) => 0x0004,
+
+ InvalidShortParameter( ..) => 0x1000,
+ MissingInputFile => 0x1001,
+ MissingTargetProcessor => 0x1002,
+ MissingShortValue( ..) => 0x1003,
+ InvalidTargetFormat( ..) => 0x1004,
+ InvalidTargetProcessor(..) => 0x1005,
+ InvalidLongParameter( ..) => 0x1006,
+ MissingLongValue( ..) => 0x1007,
+ MissingShortArgument => 0x1008,
+ InvalidFileEncoding( ..) => 0x1009,
+ MissingLongArgument => 0x100A,
+
+ AccessDenied( ..) => 0x2000,
+ };
+ }
+
+ #[must_use]
+ pub fn message(&self) -> String {
+ use Error::*;
+
+ return match *self {
+ AccessDenied( ref f) => format!("access denied to \"{f}\""),
+ EndOfFile( _) => String::from("end of file"),
+ IllegalCharacter( c, _) => format!("illegal character U+{:04X} '{c}'", c as u32),
+ IncompleteNode( _) => format!("incomplete node"),
+ InvalidFileEncoding( ref s) => format!("invalid file encoding \"{s}\""),
+ InvalidLongParameter( ref s) => format!("invalid target processor `--{s}`"),
+ InvalidShortParameter( c) => format!("invalid short parameter `-{c}`"),
+ InvalidTargetFormat( ref s) => format!("invalid target format \"{s}\""),
+ InvalidTargetProcessor(ref s) => format!("invalid target processor \"{s}\""),
+ MissingInputFile => String::from("missing input file"),
+ MissingLongArgument => String::from("missing long argument after `--"),
+ MissingLongValue( ref s) => format!("missing value for long parameter `--{s}`"),
+ MissingShortArgument => format!("missing short parameter after `-`"),
+ MissingShortValue( c) => format!("missing value for short parameter `-{c}`"),
+ MissingTargetProcessor => String::from("missing target processor"),
+ UnknownMnemonic( ref s, _) => format!("invalid mnemonic {s}"),
+ UnterminatedString( _) => format!("unterminated string"),
+ };
+ }
+
+ #[must_use]
+ pub fn note(&self) -> Option<String> {
+ use Error::*;
+
+ return match *self {
+ EndOfFile( ref f) => Some(format!("consider adding an END directive to \"{f}\"")),
+ IllegalCharacter( _, ref l) => Some(format!("{l}")),
+ IncompleteNode( ref l) => Some(format!("{l}")),
+ UnknownMnemonic( _, ref l) => Some(format!("{l}")),
+ UnterminatedString(ref l) => Some(format!("found string delimiter {l}")),
+
+ | InvalidTargetFormat( _)
+ | InvalidTargetProcessor(_)
+ | MissingLongArgument
+ | MissingShortArgument
+ => Some(String::from("see `eas -h` or refer to the manual for more information")),
+
+ _ => None,
+ };
+ }
+}
diff --git a/src/format.rs b/src/format.rs
index b8ebd74..3030e0d 100644
--- a/src/format.rs
+++ b/src/format.rs
@@ -1,25 +1,27 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
+use crate::error::Error;
+
use enum_iterator::Sequence;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
@@ -46,14 +48,16 @@ impl Default for Format {
}
impl FromStr for Format {
- type Err = String;
+ type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use Format::*;
- return match s.to_string().to_lowercase().as_str() {
+
+ let s = s.to_string().to_lowercase();
+ return match s.as_str() {
"elf" => Ok(Elf),
- _ => Err(format!("invalid format \"{s}\"")),
+ _ => Err(Error::InvalidTargetFormat(s)),
};
}
}
diff --git a/src/is_valid_character.rs b/src/is_valid_character.rs
index 7a8201f..70cfa03 100644
--- a/src/is_valid_character.rs
+++ b/src/is_valid_character.rs
@@ -1,22 +1,22 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
@@ -26,12 +26,11 @@ pub fn is_valid_character(c: char) -> bool {
| '\t'
| '\n'
| ' '
- | '!'
- | '"'
- | '#'
- | '*'
- | ','
- | '.'
+ | '!' // Some operands
+ | '"' // Strings
+ | '#' // Numeric litterals
+ | ',' // Operand separators
+ | '.' // Floating-point literals
| '0'
| '1'
| '2'
@@ -42,9 +41,7 @@ pub fn is_valid_character(c: char) -> bool {
| '7'
| '8'
| '9'
- | ':'
- | ';'
- | '@'
+ | ';' // Comments
| 'A'
| 'B'
| 'C'
diff --git a/src/log.rs b/src/log.rs
index 48de096..c72c944 100644
--- a/src/log.rs
+++ b/src/log.rs
@@ -1,41 +1,36 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. 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)?));
- }};
+ (error, $error: expr) => {{
+ use crate::error::Error;
+ let error: Error = $error;
+ eprintln!("\u{1B}[1m\u{1B}[91merror\u{1B}[0m \u{1B}[1m(e{:04X})\u{1B}[22m: {}", error.code(), error.message());
- (note, $($message: tt)*) => {{q
- eprintln!("\u{1B}[1m\u{1B}[95mnote\u{1B}[0m: {}", format!($($message)?));
+ if let Some(note) = error.note() { eprintln!("\u{2014} \u{1B}[1m\u{1B}[93mnote\u{1B}[0m: \u{1B}[3m{note}\u{1B}[m") };
}};
(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/node.rs b/src/node.rs
new file mode 100644
index 0000000..cd9b5cb
--- /dev/null
+++ b/src/node.rs
@@ -0,0 +1,33 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+mod encode;
+mod parse;
+
+#[derive(Clone, Debug)]
+pub enum Node {
+ Code16,
+ Code32,
+ End,
+ Fill( u64, u64, u64),
+ Space(u64),
+}
diff --git a/src/node/encode.rs b/src/node/encode.rs
new file mode 100644
index 0000000..7ae038b
--- /dev/null
+++ b/src/node/encode.rs
@@ -0,0 +1,56 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::encode_state::EncodeState;
+use crate::node::Node;
+
+impl Node {
+ #[must_use]
+ pub fn encode(&self, _state: &EncodeState) -> Vec<u8> {
+ use Node::*;
+
+ let mut binary = Vec::new();
+
+ match *self {
+ // These don't leak into the binary:
+ | Code16
+ | Code32
+ | End
+ => {},
+
+ Fill(repeat, value, size) => {
+ let mut bytes = Vec::from(value.to_le_bytes());
+ bytes.resize(size as usize, 0x00);
+
+ for _ in 0x0..repeat { binary.extend_from_slice(&bytes) }
+ }
+
+ Space(length) => for _ in 0x0..length { binary.push(0x00) },
+
+ // In case we missed something.
+ #[allow(unreachable_patterns)]
+ _ => todo!(),
+ };
+
+ return binary;
+ }
+}
diff --git a/src/node/parse.rs b/src/node/parse.rs
new file mode 100644
index 0000000..3137e97
--- /dev/null
+++ b/src/node/parse.rs
@@ -0,0 +1,90 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::error::Error;
+use crate::node::Node;
+use crate::source_location::SourceLocation;
+use crate::token::Token;
+
+macro_rules! complete_node {
+ ($nodes: expr, $new_node: expr, $flag: ident) => {{
+ $nodes.push($new_node);
+ $flag = true;
+ continue;
+ }};
+}
+
+impl Node {
+ #[must_use]
+ pub fn parse(tokens: &[(SourceLocation, Token)]) -> Result<Vec<Self>, Error> {
+ assert!(tokens.len() > 0x0);
+
+ let mut file: Option<&str> = None;
+
+ let mut nodes: Vec<Self> = Vec::new();
+
+ let mut got_end = false;
+ let mut node_complete = true;
+
+ for (location, token) in tokens {
+ use Token::*;
+
+ file = Some(location.file());
+
+ match token {
+ Word(word) => match word.as_str() {
+ | "CODE16"
+ | "code16"
+ | "THUMB"
+ | "thumb"
+ => complete_node!(nodes, Node::Code16, node_complete),
+
+ | "ARM"
+ | "arm"
+ | "CODE32"
+ | "code32"
+ => complete_node!(nodes, Node::Code32, node_complete),
+
+ | "END"
+ | "end"
+ => {
+ got_end = true;
+ complete_node!(nodes, Node::End, node_complete);
+ },
+
+ _ => return Err(Error::UnknownMnemonic(word.clone(), location.clone())),
+ },
+
+ Return => {
+ if !node_complete { return Err(Error::IncompleteNode(location.clone())) };
+ continue;
+ },
+
+ _ => {},
+ };
+ }
+
+ if !got_end { return Err(Error::EndOfFile(file.unwrap().to_string())) };
+
+ return Ok(nodes);
+ }
+}
diff --git a/src/cpu.rs b/src/processor.rs
index e9705bf..121c126 100644
--- a/src/cpu.rs
+++ b/src/processor.rs
@@ -1,52 +1,56 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
+use crate::error::Error;
+
use enum_iterator::Sequence;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Clone, Copy, Eq, PartialEq, Sequence)]
-pub enum Cpu {
+pub enum Processor {
Arm7tdmi,
}
-impl Display for Cpu {
+impl Display for Processor {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- use Cpu::*;
+ use Processor::*;
return match *self {
Arm7tdmi => write!(f, "arm7tdmi"),
};
}
}
-impl FromStr for Cpu {
- type Err = String;
+impl FromStr for Processor {
+ type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- use Cpu::*;
- return match s.to_string().to_lowercase().as_str() {
+ use Processor::*;
+
+ let s = s.to_string().to_lowercase();
+ return match s.as_str() {
"arm7tdmi" => Ok(Arm7tdmi),
- _ => Err(format!("invalid target \"{s}\"")),
+ _ => Err(Error::InvalidTargetProcessor(s)),
};
}
}
diff --git a/src/source_location.rs b/src/source_location.rs
new file mode 100644
index 0000000..47f0f4b
--- /dev/null
+++ b/src/source_location.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
+
+ This file is part of eAS.
+
+ eAS 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.
+
+ eAS 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 eAS. If not,
+ see <https://www.gnu.org/licenses/>.
+*/
+
+use std::fmt::{Display, Formatter};
+use std::num::NonZeroUsize;
+
+#[derive(Clone, Debug)]
+pub struct SourceLocation {
+ file: String,
+ line: NonZeroUsize,
+ column: NonZeroUsize,
+}
+
+impl SourceLocation {
+ pub fn new(file: &str) -> Self { Self {
+ file: file.to_string(),
+ line: NonZeroUsize::new(0x1).unwrap(),
+ column: NonZeroUsize::new(0x1).unwrap(),
+ } }
+
+ pub fn next_column(&mut self) {
+ self.column = self.column.checked_add(0x1).unwrap();
+ }
+
+ pub fn return_carriage(&mut self) {
+ self.line = self.line.checked_add(0x1).unwrap();
+ self.column = NonZeroUsize::new(0x1).unwrap();
+ }
+
+ #[inline(always)]
+ #[must_use]
+ pub fn file<'a>(&'a self) -> &'a str { &self.file }
+}
+
+impl Display for SourceLocation {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ return write!(f, "in \"{}\", at {}:{}", &self.file, self.line, self.column);
+ }
+}
diff --git a/src/token.rs b/src/token.rs
index 7470698..62cc609 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -1,22 +1,22 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
@@ -26,11 +26,10 @@ mod tokenise;
pub enum Token {
BracketLeft,
BracketRight,
- Colon,
Comma,
FullStop,
Hashtag,
Return,
StringLiteral(String),
- Word(String),
+ Word( String),
}
diff --git a/src/token/tokenise.rs b/src/token/tokenise.rs
index e713baa..743051b 100644
--- a/src/token/tokenise.rs
+++ b/src/token/tokenise.rs
@@ -1,131 +1,166 @@
/*
- Copyright 2023 Gabriel Bjørnager Jensen.
+ Copyright 2023-2024 Gabriel Bjørnager Jensen.
- This file is part of AAS.
+ This file is part of eAS.
- AAS is free software: you can redistribute it
+ eAS 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
+ eAS 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,
+ General Public License along with eAS. If not,
see <https://www.gnu.org/licenses/>.
*/
use crate::is_valid_character;
+use crate::error::Error;
+use crate::source_location::SourceLocation;
use crate::token::Token;
impl Token {
#[must_use]
- pub fn tokenise(input: &str) -> Result<Vec<Self>, String> {
- let mut tokens: Vec<Self> = Vec::new();
+ pub fn tokenise(input: &str, location: &mut SourceLocation) -> Result<Vec<(SourceLocation, Self)>, Error> {
+ let mut tokens: Vec<(SourceLocation, Self)> = Vec::new();
let mut input_index: usize = 0x0;
- while let Some(token) = get_next_token(&input, &mut input_index)? { tokens.push(token) }
+ while let Some(token) = get_next_token(&input, &mut input_index, location)? { tokens.push(token) }
return Ok(tokens);
}
}
#[must_use]
-fn get_next_token(input: &str, index: &mut usize) -> Result<Option<Token>, String> {
+fn get_next_token(input: &str, index: &mut usize, location: &mut SourceLocation) -> Result<Option<(SourceLocation, Token)>, Error> {
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))),
-
- _ => {},
- };
- }
+ if !is_valid_character(c) { return Err(Error::IllegalCharacter(c, location.clone()) ) };
// 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;
+
+ let token_start = location.clone();
+
+ match c {
+ | ' '
+ | '\t'
+ | '\n'
+ | '['
+ | ']'
+ | '.'
+ | ','
+ | '#'
+ | ';'
+ | '"'
+ => {
+ *index += 0x1;
+ location.next_column();
+ },
+
+ _ => {},
+ };
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)),
+ '\n' => {
+ location.return_carriage();
+ return Ok(Some((token_start, Return)));
+ },
- | ';'
- | '@'
- => {
- in_comment = true;
- continue;
+ '[' => return Ok(Some((token_start, BracketLeft))),
+ ']' => return Ok(Some((token_start, BracketRight))),
+ '.' => return Ok(Some((token_start, FullStop))),
+ ',' => return Ok(Some((token_start, Comma))),
+ '#' => return Ok(Some((token_start, Hashtag))),
+
+ ';' => {
+ skip_line(input, index, location);
+ return Ok(Some((token_start, Return)));
},
'"' => {
- in_string = true;
- continue;
+ return match complete_string(input, index, location) {
+ Ok(string) => Ok(Some((token_start, StringLiteral(string)))),
+ _ => Err(Error::UnterminatedString(token_start)),
+ };
}
_ => {},
};
- word.push(c);
+ match complete_word(input, index, location) {
+ Some(word) => return Ok(Some((token_start, Word(word)))),
+ _ => {},
+ };
}
- if in_string { return Err("unterminated string".to_string()) };
-
return Ok(None);
}
+
+#[must_use]
+fn complete_word(input: &str, index: &mut usize, location: &mut SourceLocation) -> Option<String> {
+ let mut buffer = String::new();
+
+ for c in input.chars().skip(*index) {
+ match c {
+ | ' '
+ | '\t'
+ | '\n'
+ | '.'
+ | ','
+ | ';'
+ => return Some(buffer),
+
+ _ => buffer.push(c),
+ }
+
+ // Don't count the terminating character.
+ *index += 0x1;
+ location.next_column();
+ }
+
+ return None;
+}
+
+#[must_use]
+fn complete_string(input: &str, index: &mut usize, location: &mut SourceLocation) -> Result<String, ()> {
+ let mut buffer = String::new();
+
+ for c in input.chars().skip(*index) {
+ *index += 0x1;
+
+ match c {
+ '\n' => return Err(()),
+ '"' => return Ok(buffer),
+ _ => {},
+ };
+
+ location.next_column();
+
+ buffer.push(c);
+ }
+
+ return Err(());
+}
+
+fn skip_line(input: &str, index: &mut usize, location: &mut SourceLocation) {
+ for c in input.chars().skip(*index) {
+ // Skip until we're out of the comment.
+ *index += 0x1;
+ if c == '\n' { break };
+ }
+
+ location.return_carriage();
+}