summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md17
-rw-r--r--Cargo.toml14
-rw-r--r--README.md10
-rw-r--r--docs/Makefile17
-rw-r--r--docs/aas.tex270
-rw-r--r--docs/eas.svg21
-rw-r--r--docs/eas.tex567
-rw-r--r--eas.svg20
-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
-rw-r--r--test.s20
32 files changed, 1616 insertions, 588 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e0387b..39b15e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,20 @@
+# 0.3.0
+
+* Bump minor version
+* Update manual
+* Update crate description
+* Update copyright years
+* Prioritise armasm compatibility
+* Rename project to eAS (was AAS)
+* Add logo
+* Add trademark notices to manual
+* Update readme
+* Support long arguments
+* Add configuration structure
+* Refactor tokeniser
+* Bump dependency versions
+* Restructure code
+
# 0.2.0
* Bump minor version
diff --git a/Cargo.toml b/Cargo.toml
index 40c615f..aeb56cd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,18 +1,18 @@
[package]
-name = "aas"
-version = "0.2.0"
+name = "eAS"
+version = "0.3.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
-description = "Advanced Arm Assembler."
-repository = "https://mandelbrot.dk/aas"
+description = "The Embedded Assembler."
+repository = "https://mandelbrot.dk/eas"
[[bin]]
-name = "aas"
-path = "src/aas.rs"
+name = "eas"
+path = "src/eas.rs"
[profile.release]
codegen-units = 1
lto = "fat"
[dependencies]
-enum-iterator = "1.4.1"
+enum-iterator = "1.5.0"
diff --git a/README.md b/README.md
index 381dd32..881669a 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,9 @@
-# AAS
+# eAS
-Compile the manual using `mkdir -p docs/build && pdflatex --output-directory="docs/build" "docs/aas.tex"`.
+eAS - the *Embedded Assembler* - is an open-source cross-assembler for embedded devices using the Arm instruction set architecture.
+
+The assembler is primarily targeted at UNIX systems - however - other platforms may be considered in the future.
+
+For more information, see the manual.
+Compile it using the provided makefile via `make -BCdocs`.
+Rerun as needed.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..cdda7dc
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,17 @@
+TEXARGS := --output-directory=build
+
+BUILDDIR := build
+
+DOC := eas
+
+LOGO := eas
+
+$(BUILDDIR)/$(DOC).pdf: $(DOC).tex $(BUILDDIR)/$(DOC).png $(BUILDDIR)
+ pdflatex $(TEXARGS) $(DOC).tex
+ pdflatex $(TEXARGS) $(DOC).tex
+
+$(BUILDDIR)/eas.png: $(LOGO).svg $(BUILDDIR)
+ inkscape --export-type=png --export-width=1024 --export-filename=$(BUILDDIR)/$(LOGO).png $(LOGO).svg
+
+$(BUILDDIR):
+ mkdir -p $(BUILDDIR)
diff --git a/docs/aas.tex b/docs/aas.tex
deleted file mode 100644
index f688f53..0000000
--- a/docs/aas.tex
+++ /dev/null
@@ -1,270 +0,0 @@
-\documentclass[a4paper]{article}
-
-\usepackage[english]{babel}
-\usepackage{fancyhdr}
-\usepackage[T1]{fontenc}
-\usepackage[margin=2cm]{geometry}
-\usepackage[hidelinks, pdfusetitle]{hyperref}
-\usepackage{lastpage}
-\usepackage{parskip}
-\usepackage{titlesec}
-\usepackage{varwidth}
-
-\newcommand{\aasmajor}{0}
-\newcommand{\aasminor}{2}
-\newcommand{\aaspatch}{0}
-
-\newcommand{\documenttitle}{Using AAS}
-
-\title{\documenttitle}
-\author{Gabriel Bjørnager Jensen}
-\date{2023-12-31}
-
-\pagestyle{fancy}
-\fancyhf{}
-\renewcommand{\headrulewidth}{0pt}
-\fancyhead[c]{\bfseries \documenttitle}
-\fancyfoot[c]{Page $\thepage\over\pageref{LastPage}$}
-
-\titleformat{\section}{\huge\bfseries}{\thesection}{1ex}{}{}
-\titleformat{\subsection}{\large\bfseries}{\thesubsection}{1ex}{}{}
-
-\begin{document}
- \pagenumbering{gobble}
- \thispagestyle{empty}
-
- \vspace*{\fill}
- \begin{center}
- {\huge\bfseries\documenttitle}
-
- {\large\aasmajor.\aasminor.\aaspatch}
- \end{center}
- \vspace*{\fill}
-
- \clearpage
- \pagenumbering{arabic}
- \begin{center}
- Copyright © 2023 Gabriel Bjørnager Jensen.
-
- This manual is licensed under a Creative Commons Attribution-ShareAlike-4.0 International license.
-
- See more at \url{https://creativecommons.org/licenses/by-sa/4.0/}.
- \end{center}
-
- \clearpage
- \tableofcontents
-
- \clearpage
- \section{About AAS}
- AAS -- the \textit{Advanced Arm Assembler} -- is an open-source cross-assembler for the ARM Instruction Set Architecture.
-
- \subsection{Copyright \& License}
- AAS is copyright © 2023 Gabriel Bjørnager Jensen.
-
- 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 and/or this manual. If not, see \url{https://www.gnu.org/licenses/}.
-
- \clearpage
- \section{Setup}
- \subsection{Download}
- AAS may be downloaded in source form from one of the following official mirrors:
-
- \begin{center}
- \begin{varwidth}{\linewidth}
- \url{https://mandelbrot.dk/aas}
-
- \url{https://gitlab.com/bjoernager/aas.git}
-
- \url{https://github.com/bjoernager/aas.git}
- \end{varwidth}
- \end{center}
-
- \subsection{Installation}
- First, make sure to meet the prerequisites for installing AAS:
-
- \begin{itemize}
- \item Having installed \textit{Rust} and the build system \textit{Cargo}
- \begin{itemize}
- \item Install either of the \texttt{rust} or \texttt{rustup} packages using pacman (Arch Linux)
- \item Install the \texttt{lang/rust} package (FreeBSD)
- \item View the official Rust documentation at \url{https://www.rust-lang.org/tools/install/}.
- \end{itemize}
- \item Having compiled the AAS executable \textbf{using the \textit{release} profile}
- \begin{itemize}
- \item Run \texttt{cargo build -{}-release}
- \end{itemize}
- \end{itemize}
-
- Then, to install the executable, simply execute:
-
- \begin{center}
- \begin{varwidth}{\linewidth}
- \ttfamily
- install -m755 \$\{BINDIR\} "target/release/aas"
- \end{varwidth}
- \end{center}
-
- Wherein \texttt{\$\{BINDIR\}} denotes the destination directory, usually \texttt{"/usr/bin"}.
-
- A PKGBUILD for AAS will likely be provided in the near future at \url{https://mandelbrot.dk/pkgbuild_aas}.
-
- \clearpage
- \section{Usage}
- Invoke the assembler using the \texttt{aas} command:
-
- \begin{center}
- \ttfamily
- aas \textit{[options]} <input>
- \end{center}
-
- Wherein \textit{options} may be any combination of the following parameters:
-
- \begin{itemize}
- \item -f\quad{} Sets the target executable format
- \item -h\quad{} Prints help
- \item -m\quad{} Sets the target CPU
- \item -v\quad{} Prints the version number
- \end{itemize}
-
- \subsection{Supported Target CPUs}
- Only the following identifier is supported when provided to the \texttt{-m} parameter:
-
- \begin{itemize}
- \item arm7tdmi
- \end{itemize}
-
- \subsection{Supported Target Executable Formats}
- Only the following identifier is supported when provided to the \texttt{-f} parameter:
-
- \begin{itemize}
- \item elf
- \end{itemize}
-
- \clearpage
- \section{Syntax}
- The AAS syntax is designed to be largely compatible with the GAS syntax as well as that of the official armasm assembler.
-
- Comments are denoted with either a \texttt{;} (semicolon) or an \texttt{@} (commercial at). These comments continue until the end of the current line. Multi-line comments are currently not supported.
-
- \begin{center}
- \begin{varwidth}{\linewidth}
- \begin{verbatim}
- ; This is a comment.
- @ This is also a comment.
- MOV r0, pc ; This line will be parsed up till the semicolon.
- /*
- This is an error (for now).
- */
- \end{verbatim}
- \end{varwidth}
- \end{center}
-
- An identifier prepended with a \texttt{.} (full stop) denotes an assembler directive\footnote{However, if the identifier is also appended with a \texttt{:} (colon), it denotes a label instead.}:
-
- \begin{center}
- \begin{varwidth}{\linewidth}
- \begin{verbatim}
- .BYTE #0x7F ; This embeds the value 127 using eight bits.
- .THUMB ; All code after this line will be assembled as Thumb code.
- \end{verbatim}
- \end{varwidth}
- \end{center}
-
- An identifier appended with a \texttt{:} (colon) denotes a label:
-
- \begin{center}
- \begin{varwidth}{\linewidth}
- \begin{verbatim}
- start: ; This is a label.
- _start: ; This is also a label.
- .start: ; This is a label as well.
- \end{verbatim}
- \end{varwidth}
- \end{center}
-
- \subsection{Accepted Directives}
- \subsubsection{ARM}
- \textit{Usage: \texttt{.ARM}}
-
- Specifies that all following code be assembled into ARM (32-bit) opcodes. May be overriden by a new \texttt{.THUMB} directive.
-
- Thumb-exclusive instructions are translated to their equivalent ARM opcode: For example, the following two instructions are identical:
-
- \begin{center}
- \begin{varwidth}{\linewidth}
- \begin{verbatim}
- .ARM
- ASR r0, r1
- MOV r0, r0, ASR r1
- \end{verbatim}
- \end{varwidth}
- \end{center}
-
- \subsubsection{BYTE}
- \textit{Usage: \texttt{.BYTE <value>}}
-
- Embeds the 8-bit value \textit{value} into the executable.
-
- The range of valid values lies in $([0;2^8-1]=[0;255])$.
-
- \subsubsection{DOUBLEWORD}
- \textit{Usage: \texttt{.DOUBLEWORD <value>}}
-
- Embeds the 64-bit value \textit{value} into the executable.
-
- The range of valid values lies in $([0;2^{64}-1]=[0;18446744073709551615])$.
-
- \subsubsection{GLOBAL}
- \textit{Usage: \texttt{.GLOBAL}}
-
- Specifies that the following label shall be externally visible.
-
- \subsubsection{HALFOWRD}
- \textit{Usage: \texttt{.HALFWORD <value>}}
-
- Embeds the 16-bit value \textit{value} into the executable.
-
- The range of valid values lies in $([0;2^{16}-1]=[0;65535])$.
-
- \subsubsection{THUMB}
- \textit{Usage: \texttt{.THUMB}}
-
- Specifies that all following code be assembled into Thumb (16-bit) opcodes. May be overriden by a new \texttt{.ARM} directive.
-
- \subsubsection{WORD}
- \textit{Usage: \texttt{.WORD <value>}}
-
- Embeds the 32-bit value \textit{value} into the executable.
-
- The range of valid values lies in $([0;2^{32}-1]=[0;4294967295])$.
-
- \subsection{Character Set}
- AAS requires that all input files be encoded in UTF-8 \textbf{only}.
-
- Outside of strings and comments, only a small subset of ASCII characters are allowed (see figure \ref{fig:characters}). The presence of any character outside of this subset will yield in an error.
-
- \begin{figure}[h]
- \centering
-
- \caption{ASCII-subset of which character are valid outside strings and comments.}
- \label{fig:characters}
- \begin{tabular}{|c|c c c c c c c c c c c c c c c c|}
- \hline
- {} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & A & B & C & D & E & F \\
- \hline
- 0 & \textit{NUL} & {} & {} & {} & {} & {} & {} & {} & {} & \textit{HT} & \textit{LF} & {} & {} & {} & {} & {} \\
- 1 & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} \\
- 2 & \textit{SP} & ! & " & \# & {} & {} & {} & {} & {} & {} & * & {} & , & {} & . & {} \\
- 3 & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & : & ; & < & {} & > & {} \\
- 4 & @ & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O \\
- 5 & P & Q & R & S & T & U & V & W & X & Y & Z & [ & {} & ] & {} & \_ \\
- 6 & {} & a & b & c & d & e & f & g & h & i & j & k & l & m & n & o \\
- 7 & p & q & r & s & t & u & v & w & x & y & z & {} & {} & {} & {} & {} \\
- \hline
- \end{tabular}
- \end{figure}
-
-\end{document}
diff --git a/docs/eas.svg b/docs/eas.svg
new file mode 100644
index 0000000..7b50ce2
--- /dev/null
+++ b/docs/eas.svg
@@ -0,0 +1,21 @@
+<!--
+ This file is licensed under a Creative Commons Attribution-ShareAlike 4.0 International license.
+ See more at <https://creativecommons.org/licenses/by-sa/4.0/>.
+-->
+<!-- For use in the eAS manual. -->
+<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
+ <!-- masks -->
+ <mask id="strike">
+ <circle cx="48" cy="48" fill="white" r="36" />
+ <polygon fill="black" points="96,0 96,96 0,96" />
+ </mask>
+ <mask id="outline">
+ <rect fill="white" height="96" mask="url(#outline)" width="96" x="0" y="0" />
+ <circle cx="48" cy="48" fill="black" r="36" />
+ <circle cx="48" cy="48" fill="white" r="24" />
+ <polygon fill="white" points="96,0 96,48 48,48" />
+ <polyline mask="url(#strike)" points="96,0 0,96" stroke="black" stroke-width="24" />
+ </mask>
+ <!-- fills -->
+ <rect fill="#000000" height="96" mask="url(#outline)" rx="12" width="96" x="0" y="0" />
+</svg>
diff --git a/docs/eas.tex b/docs/eas.tex
new file mode 100644
index 0000000..5d96577
--- /dev/null
+++ b/docs/eas.tex
@@ -0,0 +1,567 @@
+\documentclass[a4paper]{article}
+
+\newcommand*{\eAS}{\textup{\textit{e}AS}}
+
+\newcommand*{\easversion}{\oldstylenums{0.3.0}}
+
+\newcommand*{\documenttitle}{Using \eAS}
+\newcommand*{\documentdate}{\oldstylenums{2024}-\oldstylenums{02}-\oldstylenums{04}}
+
+\newcommand*{\rulewidth}{0.4pt}
+
+\makeatletter
+\newcommand*{\@usagevalue}[1]{\textit{<#1>}}
+\newcommand*{\@usagevaluestar}[1]{\{\textit{<#1>}\}}
+\newcommand*{\usagevalue}{\@ifstar{\@usagevaluestar}{\@usagevalue}}
+\newcommand*{\usage}[1]{Usage: \fbox{\texttt{#1}}}
+\makeatother
+
+\newenvironment{codeblock}{\mdframed\verbatim}{\endverbatim\endmdframed}
+
+\newenvironment{alertentry}[2]{{\bfseries#1\newline\itshape\tiny#2}\par\small}{}
+
+\usepackage[margin=2cm]{geometry} % Set page size.
+
+\usepackage[british]{babel} % Localise for English
+
+\usepackage{array}
+\usepackage[font={it}, labelfont={bf}]{caption} % Customise captions
+\usepackage{float} % Customise floats
+\usepackage[T1]{fontenc} % Encode special characters
+\usepackage{graphicx} % Embed images
+\usepackage[hidelinks, pdfusetitle]{hyperref} % Create hyperlinks
+\usepackage{lastpage} % Refer to the last page
+\usepackage{mdframed} % Make frames
+\usepackage{nicefrac} % Making "diagonal" fractions
+\usepackage{parskip} % Disable indentations after paragraphs
+\usepackage{varwidth} % Truncate to content width
+\usepackage{verbatim} % Extend verbatims
+
+\usepackage{fancyhdr} % Set headers and footers
+ \pagestyle{fancy}
+ \fancyhf{}
+ \renewcommand*{\headrulewidth}{\rulewidth}
+ \renewcommand*{\footrulewidth}{\rulewidth}
+ \renewcommand*{\sectionmark}[1]{\markboth{\thesection\ #1}{}}
+ \fancyhead[l]{\bfseries\documenttitle}
+ \fancyhead[c]{\bfseries v. \easversion}
+ \fancyhead[r]{\bfseries \nouppercase{\leftmark}} % CONTENTS will be uppercase by default.
+ \fancyfoot[c]{\bfseries Page \nicefrac{\thepage}{\pageref*{LastPage}}}
+
+\usepackage{multicol} % Format in multiple columns
+ \setlength{\columnsep}{1cm}
+ \setlength{\columnseprule}{\rulewidth}
+
+\usepackage{titlesec} % Format heading
+ \titleformat{\section}{\huge\scshape\bfseries}{\thesection}{1ex}{}{}
+ \titleformat{\subsection}{\large\bfseries}{\thesubsection}{1ex}{}{}
+
+\title{\documenttitle}
+\author{Gabriel Bjørnager Jensen}
+\date{\documentdate}
+
+\begin{document}
+ \pagenumbering{gobble} % Don't count the first page.
+ \thispagestyle{empty} % We also don't need any headers or footers here.
+
+ \vspace*{\fill}
+ \begin{center}
+ {\huge\bfseries\documenttitle}
+
+ \includegraphics[interpolate, width=6cm]{build/eas.png} % This is build by the makefile.
+
+ {\huge\bfseries\easversion}
+ \end{center}
+ \vspace*{\fill}
+
+ \clearpage
+ \thispagestyle{empty} % This page is also unique.
+ \begin{center}
+ Copyright © \oldstylenums{2023}-\oldstylenums{2024} Gabriel Bjørnager Jensen.
+
+ This manual is licensed under a Creative Commons Attribution-ShareAlike \oldstylenums{4.0} International license.
+ See more at \url{https://creativecommons.org/licenses/by-sa/4.0/}.
+
+ The \eAS\ logotype ``e'' is licensed under a Creative Commons Attribution-ShareAlike \oldstylenums{4.0} International license.
+ See more at \url{https://creativecommons.org/licenses/by-sa/4.0/}.
+
+ \rule{\linewidth}{\rulewidth}
+
+ \textit{Arm}, \textit{Thumb}, \textit{Arm7}, \textit{Arm7TDMI}, \textit{Arm7TDMI-S}, and \textit{Arm7EJ-S} are registered trademarks or trademarks of Arm Limited (or its subsidiaries or affiliates).
+ \textit{UNIX} is a registered trademark of The Open Group Limited.
+
+ The \eAS\ project is \textit{not} affiliated with Arm Limited and/or The Open Group Limited.
+
+ \vspace*{\fill}
+
+ This manual was written using \LaTeX.
+
+ \documentdate
+ \end{center}
+
+ \clearpage
+ \pagenumbering{arabic} % Now we can number.
+ \tableofcontents
+
+ \clearpage
+ \section{About \eAS}
+ \label{sec:about_eas}
+ \eAS\ (\textit{/i:z/} as in ``ease'') -- the \textit{Embedded Assembler} -- is an open-source cross-assembler for embedded devices using the Arm instruction set architecture.
+
+ The assembler is primarily targeted at UNIX systems -- however -- other platforms may be considered in the future (see section \ref{sec:setup:machine_requirements}).
+
+ \begin{center}
+ \begin{tabular}{|l|l|l|}
+ \multicolumn{3}{c}{\itshape\bfseries Consider checking out some of my related projects:} \\
+ \multicolumn{3}{c}{} \\
+ \hline
+ \multicolumn{1}{|c|}{\bfseries Title} & \multicolumn{1}{c|}{\bfseries Description} & \multicolumn{1}{c|}{\bfseries URL} \\
+ agbsum & AGB image header checksum patcher. & \url{https://mandelbrot.dk/agbsum} \\
+ AX & AGB development framework. & \url{https://mandelbrot.dk/ax} \\
+ Luma & AGB emulator. & \url{https://mandelbrot.dk/luma} \\
+ \hline
+ \end{tabular}
+ \end{center}
+
+ \subsection{Rationale}
+ \label{sec:rationale}
+ I had wanted to write an assembler for the Arm instruction set architecture that:
+
+ \begin{itemize}
+ \item Was open-source
+ \item Had a clean and readable codebase
+ \item Was simple to use and not overly complex (a.k.a. lived up to the ``Unix Philosophy'')
+ \item Could more or less replace armasm (as an open-source alternative)
+ \item Could directly target embedded devices, e.g. the Game Boy Advance (and potentially later consoles)
+ \item \textit{Bonus} --- Could also target hosted systems, e.g. the Raspberry PI running FreeBSD
+ \end{itemize}
+
+ It is easiest to be comfortable with a program which you have written yourself.
+ Or, as Ken Thompson put it:
+
+ \begin{center}
+ \fbox{\centering\itshape\bfseries You can't trust code that you did not totally create yourself.}
+ \end{center}
+
+ Altough this journey is also for the sake of my own experiences, \eAS\ was one of my first projects intended for real-world applications.
+
+ \subsection{Copyright \& License}
+ \label{sec:copyright_and_license}
+ \eAS\ is copyright © \oldstylenums{2023}-\oldstylenums{2024} Gabriel Bjørnager Jensen.
+
+ \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\ and/or this manual.
+ If not, see \url{https://www.gnu.org/licenses/}.
+
+ \clearpage
+ \section{Project Changelog}
+ \label{sec:project_changelog}
+ \begin{multicols*}{2}
+ \subsection*{Version 0.3.0}
+ \label{sec:project_changelog:version_0-3-0}
+ \begin{itemize}
+ \item Bump minor version
+ \item Update manual
+ \item Update crate description
+ \item Update copyright years
+ \item Prioritise armasm compatibility
+ \item Rename project to eAS (was AAS)
+ \item Add logo
+ \item Add trademark notices to manual
+ \item Update readme
+ \item Support long arguments
+ \item Add configuration structure
+ \item Refactor tokeniser
+ \item Bump dependency versions
+ \item Restructure code
+ \end{itemize}
+
+ \subsection*{Version 0.2.0}
+ \label{sec:project_changelog:version_0-2-0}
+ \begin{itemize}
+ \item Bump minor version
+ \item Update and fix manual
+ \item Update copyright notices
+ \item Change acronym meaning
+ \end{itemize}
+
+ \subsection*{Version 0.1.0}
+ \label{sec:project_changelog:version_0-1-0}
+ \begin{itemize}
+ \item Bump minor version
+ \item Add manual
+ \item Update gitignore
+ \item Add readme
+ \item Update license notices
+ \end{itemize}
+
+ \subsection*{Version 0.0.0}
+ \label{sec:project_changelog:version_0-0-0}
+ \begin{itemize}
+ \item Add gitignore
+ \item Add test source file
+ \item Implement tokeniser
+ \item Add license file
+ \item Add changelog
+ \end{itemize}
+ \end{multicols*}
+
+ \clearpage
+ \section{Setup}
+ \label{sec:setup}
+ \subsection{Machine Requirements}
+ \label{sec:setup:machine_requirements}
+ \eAS\ has -- at the moment -- only been tested to work on Arch Linux.
+ However, support is planned for most mainstream UNIX systems and Linux distributions.
+
+ \subsection{Download}
+ \label{sec:setup:download}
+ \eAS\ may be downloaded in source form from one of the following official mirrors:
+
+ \begin{figure}[H]
+ \centering
+
+ \caption{Official mirrors.}
+ \label{fig:mirrors}
+
+ \begin{tabular}{l>{\itshape}l}
+ \hline
+ \multicolumn{1}{>{\bfseries}c}{URL} & \multicolumn{1}{>{\bfseries}c}{Remarks} \\
+ \hline
+ \url{https://mandelbrot.dk/eas} & The ``main'' mirror \\
+ \url{https://gitlab.com/bjoernager/eas.git} & {} \\
+ \url{https://github.com/bjoernager/eas.git} & {} \\
+ \hline
+ \end{tabular}
+ \end{figure}
+
+ Thanks to GitHub and GitLab for providing the additional (unaffiliated) mirrors.
+
+ \subsection{Installation}
+ \label{sec:setup:installation}
+ First, make sure to meet the prerequisites for installing \eAS\:
+
+ \begin{itemize}
+ \item Having installed \textit{Rust} and the build system \textit{Cargo}
+ \begin{itemize}
+ \item Install either of the \texttt{rust} or \texttt{rustup} packages using pacman (Arch Linux)
+ \item Install the \texttt{lang/rust} package (FreeBSD)
+ \item Read the official Rust documentation at \url{https://www.rust-lang.org/tools/install/}.
+ \end{itemize}
+ \item Having compiled the \eAS\ executable \textbf{using the \textit{release} profile}
+ \begin{itemize}
+ \item Run \texttt{cargo build -{}-release}
+ \end{itemize}
+ \end{itemize}
+
+ Then, to install the executable, simply execute:
+
+ \begin{center}
+ \begin{varwidth}{\linewidth}
+ \ttfamily
+ install -m755 "\$\{BINDIR\}" "target/release/eas"
+ \end{varwidth}
+ \end{center}
+
+ wherein \texttt{\$\{BINDIR\}} denotes the destination directory, usually \texttt{"/usr/bin"}.
+
+ A PKGBUILD for \eAS\ will likely be provided in the near future at \url{https://mandelbrot.dk/pkgbuild_eas}.
+
+ \clearpage
+ \section{Usage}
+ \label{sec:usage}
+ Invoke the assembler using the \texttt{eas} command (or a path to the executable):
+
+ \begin{center}
+ \fbox{\ttfamily eas \usagevalue*{options} \usagevalue{input}}
+ \end{center}
+
+ wherein \texttt{options} is an optional combination of any of the following arguments:
+
+ \begin{tabular}{|>{\ttfamily}l|l}
+ -c\usagevalue{encoding} & Sets the expected input file encoding (character set) to \textit{encoding} \\
+ -f\usagevalue{format} & Sets the target executable format to \textit{format} \\
+ -m\usagevalue{processor} & Sets the target processor (machine) to \textit{processor} \\
+ \multicolumn{2}{c}{} \\
+ -{}-help & Prints help \\
+ -{}-version & Prints versioning
+ \end{tabular}
+
+ Short arguments may be chained together, however, only the last in a chain may be transitive.
+
+ \subsection{File Encodings}
+ \label{sec:usage:file_encodings}
+ Only the following identifier is supported when provided to the \texttt{-c} parameter:
+
+ \begin{itemize}
+ \item \texttt{utf8} \textit{--- default}
+ \end{itemize}
+
+ \subsection{Target Processors}
+ \label{sec:usage:target_processors}
+ Only the following identifier is supported when provided to the \texttt{-m} parameter:
+
+ \begin{itemize}
+ \item \texttt{arm7tdmi}
+ \end{itemize}
+
+ \subsection{Target Executable Formats}
+ \label{sec:usage:target_executable_formats}
+ Only the following identifier is supported when provided to the \texttt{-f} parameter:
+
+ \begin{itemize}
+ \item \texttt{elf} \textit{--- default}
+ \end{itemize}
+
+ \clearpage
+ \section{Syntax}
+ \label{sec:syntax}
+ The syntax of \eAS\ assembly is designed to be largely compatible with that of the official \textit{armasm} assembler.
+
+ Each line of an assembly source file is a \textit{node} and has the following format:
+
+ \begin{center}
+ \begin{varwidth}{\textwidth}
+ \ttfamily
+ \usagevalue*{label} \usagevalue*{instruction} \{; \usagevalue{comment}\}
+ \end{varwidth}
+ \end{center}
+
+ where each part -- the \textit{label}, the \textit{instruction}, and the \textit{comment} -- are optional and may (usually, see below) be provided in any combination.
+
+ A \textit{label} is an identifier that will be assigned to the resulting memory address.
+ Labels are often used to name code blocks (so-called \textit{functions} or \textit{subroutines}), but also variables and constants.
+ Some assembler directives, such as \texttt{ALIGN} (see section \ref{sec:directives:align}), do not accept a label.
+ See each directive's own entry for more information.
+
+ \textit{Instruction} is here used as an umbrella term for actual instructions of Arm architectures, pseudo-instructions, and assembler directives.
+ \textit{Comments} are described in detail in section \ref{sec:syntax:comments}.
+
+ \subsection{Comments}
+ \label{sec:syntax:comments}
+ Comments are denoted with a \texttt{;} (semicolon) delimiter, and continue until the end of the current line.
+ Multi-line comments are not supported.
+
+ \begin{codeblock}
+ @ This is not comment.
+ // This is also not a comment.
+ /*
+ This is also an error.
+ */
+
+ ; This is a comment.
+ MOV r0, pc ; This line will be parsed up till the semicolon.
+ \end{codeblock}
+
+ \subsection{Character Set}
+ \label{sec:syntax:character_set}
+ \eAS\ requires that all input files be encoded in UTF-8 \textbf{only}.
+
+ Outside of strings and comments, only a small subset of ASCII characters are allowed (see figure \ref{fig:character_set}).
+ The presence of any character outside of this subset will yield in an error.
+
+ \begin{figure}[H]
+ \centering
+
+ \caption{ASCII-subset of which character are valid outside strings and comments.}
+ \label{fig:character_set}
+
+ \begin{tabular}{|c|c c c c c c c c c c c c c c c c|}
+ \hline
+ {} & \textbf{0} & \textbf{1} & \textbf{2} & \textbf{3} & \textbf{4} & \textbf{5} & \textbf{6} & \textbf{7} & \textbf{8} & \textbf{9} & \textbf{A} & \textbf{B} & \textbf{C} & \textbf{D} & \textbf{E} & \textbf{F} \\
+ \hline
+ 0 & \textit{NUL} & {} & {} & {} & {} & {} & {} & {} & {} & \textit{HT} & \textit{LF} & {} & {} & {} & {} & {} \\
+ 1 & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} & {} \\
+ 2 & \textit{SP} & ! & " & \# & {} & {} & {} & {} & {} & {} & {} & {} & , & {} & {} & {} \\
+ 3 & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & {} & ; & {} & {} & {} & {} \\
+ 4 & {} & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O \\
+ 5 & P & Q & R & S & T & U & V & W & X & Y & Z & [ & {} & ] & {} & \_ \\
+ 6 & {} & a & b & c & d & e & f & g & h & i & j & k & l & m & n & o \\
+ 7 & p & q & r & s & t & u & v & w & x & y & z & {} & {} & {} & {} & {} \\
+ \hline
+ \end{tabular}
+ \end{figure}
+
+ \clearpage
+ \section{Directives}
+ \label{sec:directives}
+ \subsection{ALIGN}
+ \label{sec:directives:align}
+ \usage{ALIGN \usagevalue{alignment}}
+
+ Aligns to the next \textit{alignment}-bit boundary.
+ The alignment \textit{alignment} must be a power of two.
+
+ \subsection{CODE16, THUMB}
+ \label{sec:directives:code16}
+ \usage{CODE16|THUMB}
+
+ Indicates that all future code -- until otherwise noted -- should be translated to Thumb instructions.
+
+ \subsection{CODE32, ARM}
+ \label{sec:directives:code32}
+ \usage{CODE32|ARM}
+
+ Indicates that all future code -- until otherwise noted -- should be translated to ARM instructions.
+
+ \subsection{FILL}
+ \label{sec:directives:fill}
+ \usage{\usagevalue*{label} FILL \usagevalue{repeat}\{, \usagevalue{value}\{, \usagevalue{size}\}\}}
+
+ Repeats the value \textit{value} in memory as an integer of \textit{size} bytes a total of \textit{repeat} times.
+
+ If \textit{value} is omitted, and -- by extend -- \textit{size}, they are treated as being zero and one, respectively.
+ In this case, the directive is identical to the \textit{SPACE} directive (see section \ref{sec:directives:space}).
+
+ The label \textit{label} -- if present -- is assigned to destination address.
+
+ \subsection{END}
+ \label{sec:directives:end}
+ \usage{END}
+
+ Signals that the assembler has reached the end of the file.
+ An \texttt{END} directive must be present in each source file.
+
+ \subsection{ENDP, ENDFUNC}
+ \label{sec:directives:endp}
+ \usage{ENDP|ENDFUNC}
+
+ Ends the current function body.
+ An \texttt{ENDP} or \texttt{ENDFUNC} directive may not be present without an equivalent and preceding \texttt{PROC} or \texttt{FUNCTION} directive.
+
+ \subsection{PROC, FUNCTION}
+ \label{sec:directives:proc}
+ \usage{\usagevalue{label} PROC|FUNCTION}
+
+ Signals that a subroutine will follow.
+ Each \texttt{PROC} or \texttt{FUNCTION} directive must have a matching \texttt{ENDP} or \texttt{ENDFUNC} directive before the end-of-file or another subroutine.
+
+ \subsection{SPACE}
+ \label{sec:directives:space}
+ \usage{\usagevalue*{label} SPACE \usagevalue{length}}
+
+ Zero-fills memory of the length \textit{length}.
+ The \texttt{SPACE} directive is identical to the \texttt{FILL} directive with \textit{value} zero and \textit{size} one.
+
+ The label \textit{label} -- if present -- is assigned to destination address.
+
+ \begin{codeblock}
+ SPACE 0xFF
+ FILL 0xFF, 0x0, 0x1 ; Equivalent.
+ FILL 0xFF ; Also equivalent.
+ \end{codeblock}
+
+ \clearpage
+ \section{Alert Lookup}
+ \label{sec:alerts}
+ \begin{multicols*}{2}
+ \subsection{Syntax Errors}
+ \label{sec:alert_lookup:syntax_errors}
+ \begin{alertentry}{e0000}{END OF FILE}
+ Reached end-of-file before any \texttt{END} directive.
+ This directive must always be present in the end of any source file.
+
+ \textit{See section \ref{sec:directives:end} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e0001}{UNKNOWN MNEMONIC}
+ An unknown instruction, pseudo-instruction, or directive mnemonic was encountered.
+ \end{alertentry}
+
+ \begin{alertentry}{e0002}{ILLEGAL CHARACTER}
+ A character outside the allowed character set was found.
+
+ \textit{See section \ref{sec:syntax:character_set} for a list of allowed characters.}
+ \end{alertentry}
+
+ \begin{alertentry}{e0003}{INCOMPLETE NODE}
+ A node did not meet the requirements to be complete.
+
+ \textit{See section \ref{sec:syntax} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e0004}{UNTERMINATED STRING}
+ An open string did not have a matching terminating \texttt{"} (double quotation mark) character.
+ \end{alertentry}
+
+ \subsection{CLI Errors}
+ \label{sec:alert_lookup:cli_errors}
+
+ \begin{alertentry}{e1000}{INVALID SHORT PARAMETER}
+ An invalid character was provided as a short parameter.
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1001}{MISSING INPUT FILE}
+ An input file was not provided.
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1002}{MISSING TARGET PROCESSOR}
+ A processor target was not provided using the \texttt{-m} parameter.
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1003}{MISSING SHORT VALUE}
+ A transitive short parameter was provided without its value.
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1004}{INVALID TARGET FORMAT}
+ An invalid target format was provided.
+
+ \textit{See section \ref{sec:usage:target_executable_formats} for a list of valid target formats.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1005}{INVALID TARGET PROCESSOR}
+ An invalid target processor was provided.
+
+ \textit{See section \ref{sec:usage:target_processors} for a list of valid target processors.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1006}{INVALID LONG PARAMETER}
+ An invalid long parameter was proivded.
+
+ \textit{See section \ref{sec:usage} for a list of valid long parameters.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1007}{MISSING LONG VALUE}
+ A transitive long parameter was provided without a value
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1008}{MISSING SHORT ARGUMENT}
+ The lone character \texttt{-} (hyphen) was provided as an argument without any trailing short arguments.
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \begin{alertentry}{e1009}{INVALID FILE ENCODING}
+ An invalid input file format was provided.
+
+ \textit{See section \ref{sec:usage:file_encodings} for a list of valid target processors.}
+ \end{alertentry}
+
+ \begin{alertentry}{e100A}{MISSING LONG ARGUMENT}
+ A double hyphen sequence \texttt{-{}-} was provided without a long argument.
+
+ \textit{See section \ref{sec:usage} for more information.}
+ \end{alertentry}
+
+ \subsection{Internal Errors}
+ \label{sec:alert_lookup:internal_errors}
+
+ \begin{alertentry}{e2000}{ACCESS DENIED}
+ Access was denied to the input file or it doesn't exist.
+ \end{alertentry}
+ \end{multicols*}
+\end{document} \ No newline at end of file
diff --git a/eas.svg b/eas.svg
new file mode 100644
index 0000000..b02d3a8
--- /dev/null
+++ b/eas.svg
@@ -0,0 +1,20 @@
+<!--
+ This image is licensed under a Creative Commons Attribution-ShareAlike 4.0 International license.
+ See more at <https://creativecommons.org/licenses/by-sa/4.0/>.
+-->
+<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
+ <!-- masks -->
+ <mask id="strike">
+ <circle cx="48" cy="48" fill="white" r="36" />
+ <polygon fill="black" points="96,0 96,96 0,96" />
+ </mask>
+ <mask id="foreground">
+ <circle cx="48" cy="48" fill="white" r="36" />
+ <circle cx="48" cy="48" fill="black" r="24" />
+ <polygon fill="black" points="96,0 96,48 48,48" />
+ <polyline mask="url(#strike)" points="96,0 0,96" stroke="white" stroke-width="24" />
+ </mask>
+ <!-- fills -->
+ <rect fill="#01CD93" height="96" width="96" x="0" y="0" />
+ <rect fill="#00291B" height="96" mask="url(#foreground)" width="96" x="0" y="0" />
+</svg>
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();
+}
diff --git a/test.s b/test.s
index 1bab3a8..8907e6f 100644
--- a/test.s
+++ b/test.s
@@ -1,8 +1,14 @@
-_start:
- nop ; This is a comment.
- nop @ This is GNU-style comment.
- eor r0, r0, r0
- add r0, r0, #0x1
+ THUMB
+ ARM
+ CODE16
+ CODE32
+; CODE64 ; Invalid.
-.string: ; Labels and directives may share names.
- .string "Þið sjáið snæri!"
+; ALIGN 32
+;_start PROC
+; MOV r0, pc
+; NOP ; This is a comment.
+; EOR r0, r0, r0 ; Þið sjáið snæri! (Note illegal characters).
+; ADD r0, r0, #0x1
+
+ END