diff options
-rw-r--r-- | CHANGELOG.md | 17 | ||||
-rw-r--r-- | Cargo.toml | 14 | ||||
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | docs/Makefile | 17 | ||||
-rw-r--r-- | docs/aas.tex | 270 | ||||
-rw-r--r-- | docs/eas.svg | 21 | ||||
-rw-r--r-- | docs/eas.tex | 567 | ||||
-rw-r--r-- | eas.svg | 20 | ||||
-rw-r--r-- | src/app.rs | 20 | ||||
-rw-r--r-- | src/app/init.rs | 108 | ||||
-rw-r--r-- | src/app/main.rs | 32 | ||||
-rw-r--r-- | src/app/new.rs | 37 | ||||
-rw-r--r-- | src/app/print_help.rs | 33 | ||||
-rw-r--r-- | src/app/print_version.rs | 12 | ||||
-rw-r--r-- | src/app/run.rs | 41 | ||||
-rw-r--r-- | src/configuration.rs | 36 | ||||
-rw-r--r-- | src/configuration/from_arguments.rs | 153 | ||||
-rw-r--r-- | src/eas.rs (renamed from src/aas.rs) | 20 | ||||
-rw-r--r-- | src/encode_state.rs | 36 | ||||
-rw-r--r-- | src/encoding.rs | 62 | ||||
-rw-r--r-- | src/error.rs | 123 | ||||
-rw-r--r-- | src/format.rs | 20 | ||||
-rw-r--r-- | src/is_valid_character.rs | 25 | ||||
-rw-r--r-- | src/log.rs | 25 | ||||
-rw-r--r-- | src/node.rs | 33 | ||||
-rw-r--r-- | src/node/encode.rs | 56 | ||||
-rw-r--r-- | src/node/parse.rs | 90 | ||||
-rw-r--r-- | src/processor.rs (renamed from src/cpu.rs) | 30 | ||||
-rw-r--r-- | src/source_location.rs | 58 | ||||
-rw-r--r-- | src/token.rs | 13 | ||||
-rw-r--r-- | src/token/tokenise.rs | 185 | ||||
-rw-r--r-- | test.s | 20 |
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 @@ -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" @@ -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 @@ -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> @@ -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(()); +} @@ -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' @@ -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(); +} @@ -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 |