From 60115d0612b356e808a3072d386fd5bfb10dd085 Mon Sep 17 00:00:00 2001 From: vanten-s Date: Thu, 8 Aug 2024 21:53:27 +0200 Subject: [PATCH] init --- .gitignore | 2 + Cargo.toml | 6 ++ shell.nix | 13 +++++ src/assembler.rs | 13 +++++ src/error.rs | 23 ++++++++ src/main.rs | 12 ++++ src/parser.rs | 143 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 212 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 shell.nix create mode 100644 src/assembler.rs create mode 100644 src/error.rs create mode 100644 src/main.rs create mode 100644 src/parser.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e981f3c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "lisp-8bit" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..7cc9b11 --- /dev/null +++ b/shell.nix @@ -0,0 +1,13 @@ +{ pkgs ? import {} }: + +pkgs.mkShell rec { + buildInputs = with pkgs; [ + rustup + ]; + RUSTC_VERSION = "stable"; + shellHook = '' + rustup default $RUSTC_VERSION + rustup component add rust-analyzer + export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin + ''; +} diff --git a/src/assembler.rs b/src/assembler.rs new file mode 100644 index 0000000..88f255d --- /dev/null +++ b/src/assembler.rs @@ -0,0 +1,13 @@ +use crate::parser::Instruction; + +pub struct Assembler { + instruction: Instruction, +} + +impl Assembler { + pub fn assemble(instruction: Instruction) -> Vec { + let assembler = Assembler { instruction }; + + todo!() + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..e87f302 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,23 @@ +#[derive(Clone, Debug, Hash)] +pub enum Error { + SyntaxError(SyntaxError), +} + +#[derive(Clone, Debug, Hash)] +pub struct SyntaxError { + pub row: usize, + pub column: usize, + pub message: String, +} + +pub type Result = ::core::result::Result; + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::SyntaxError(e) => write!(f, "Error at: {}:{}. {}", e.row, e.column, e.message), + } + } +} + +impl std::error::Error for Error {} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4ddbd27 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,12 @@ +mod assembler; +mod parser; + +mod error; + +pub use error::*; + +fn main() { + let code = "(+ 1 2)".to_string(); + + dbg!(parser::Parser::parse(code).unwrap()); +} diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..31548f0 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,143 @@ +use crate::*; + +#[derive(Debug)] +pub struct Parser { + row: usize, + column: usize, + lines: Vec>, +} + +#[derive(Debug)] +pub(crate) struct Instruction { + name: String, + arguments: Vec, +} + +#[derive(Debug)] +enum Value { + Instruction(Instruction), + StringLiteral(String), + IntegerLiteral(usize), + Identifier(String), +} + +impl Parser { + pub fn parse(stream: String) -> Result { + let mut parser = Parser { + row: 0, + column: 0, + lines: stream + .split('\n') + .map(|a| a.chars().collect::>()) + .collect::>(), + }; + parser.instruction() + } + + fn increment(&mut self) -> Option<()> { + self.column += 1; + if self.column == self.lines[self.row].len() { + self.row += 1; + if self.row == self.lines.len() { + self.row -= 1; + self.column -= 1; + return None; + } + self.column = 0; + } + return Some(()); + } + + fn jump_whitespace(&mut self) -> Option<()> { + self.increment()?; + while self.lines[self.row][self.column].is_whitespace() { + self.increment()?; + } + Some(()) + } + + fn instruction(&mut self) -> Result { + if self.lines[self.row][self.column] != '(' { + return Err(Error::SyntaxError(SyntaxError { + row: self.row, + column: self.column, + message: "SyntaxError: Expected '('".to_string(), + })); + } + + self.jump_whitespace(); + + let name = self.identifier()?; + + let mut arguments = Vec::new(); + + while self.lines[self.row][self.column] != ')' { + match self.lines[self.row][self.column] { + '\"' => arguments.push(Value::StringLiteral(self.string_literal()?)), + '(' => arguments.push(Value::Instruction(self.instruction()?)), + character => { + if character.is_numeric() { + arguments.push(Value::IntegerLiteral(self.integer_literal()?)); + } else { + arguments.push(Value::Identifier(self.identifier()?)); + } + } + } + } + + self.jump_whitespace(); + + return Ok(Instruction { name, arguments }); + } + + fn string_literal(&mut self) -> Result { + self.increment(); + + let mut accumulator = String::new(); + + while self.lines[self.row][self.column] != '"' { + accumulator.push(self.lines[self.row][self.column]); + self.increment(); + } + + self.jump_whitespace(); + + Ok(accumulator) + } + + fn integer_literal(&mut self) -> Result { + let mut accumulator = String::new(); + + while self.lines[self.row][self.column].is_numeric() { + accumulator.push(self.lines[self.row][self.column]); + self.increment(); + } + + self.jump_whitespace(); + + match accumulator.parse::() { + Ok(v) => Ok(v), + Err(_e) => Err(Error::SyntaxError(SyntaxError { + row: self.row, + column: self.column, + message: "Couldn't parse integer literal".to_string(), + })), + } + } + + fn identifier(&mut self) -> Result { + let mut accumulator = String::new(); + + let mut current = self.lines[self.row][self.column]; + + while !"()\"".contains(current) && !current.is_whitespace() { + accumulator.push(self.lines[self.row][self.column]); + self.increment(); + current = self.lines[self.row][self.column]; + } + + self.jump_whitespace(); + + Ok(accumulator) + } +}