Migrate documentation to LaTeX

This commit is contained in:
2026-02-10 00:13:05 +01:00
parent e9bcd234e5
commit 2061fece3d
8 changed files with 446 additions and 393 deletions

6
.gitignore vendored
View File

@@ -1,7 +1,9 @@
/build/ /build/
/vendor/ /vendor/
/doc/*.html *.pdf
/doc/*.pdf *.log
*.aux
*.toc
a.out a.out

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: true
source 'https://rubygems.org'
gem 'asciidoctor', '~> 2.0'
gem 'asciidoctor-pdf', '~> 2.3'
gem "rake", "~> 13.3"

View File

@@ -1,66 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
Ascii85 (2.0.1)
addressable (2.8.8)
public_suffix (>= 2.0.2, < 8.0)
afm (1.0.0)
asciidoctor (2.0.26)
asciidoctor-pdf (2.3.24)
asciidoctor (~> 2.0)
concurrent-ruby (~> 1.3)
matrix (~> 0.4)
prawn (~> 2.4.0)
prawn-icon (~> 3.0.0)
prawn-svg (~> 0.34.0)
prawn-table (~> 0.2.0)
prawn-templates (~> 0.1.0)
treetop (~> 1.6.0)
ttfunk (~> 1.7.0)
concurrent-ruby (1.3.5)
css_parser (1.21.1)
addressable
hashery (2.1.2)
matrix (0.4.3)
pdf-core (0.9.0)
pdf-reader (2.15.0)
Ascii85 (>= 1.0, < 3.0, != 2.0.0)
afm (>= 0.2.1, < 2)
hashery (~> 2.0)
ruby-rc4
ttfunk
polyglot (0.3.5)
prawn (2.4.0)
pdf-core (~> 0.9.0)
ttfunk (~> 1.7)
prawn-icon (3.0.0)
prawn (>= 1.1.0, < 3.0.0)
prawn-svg (0.34.2)
css_parser (~> 1.6)
matrix (~> 0.4.2)
prawn (>= 0.11.1, < 3)
rexml (~> 3.2)
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
prawn-templates (0.1.2)
pdf-reader (~> 2.0)
prawn (~> 2.2)
public_suffix (7.0.0)
rake (13.3.1)
rexml (3.4.4)
ruby-rc4 (0.1.5)
treetop (1.6.18)
polyglot (~> 0.3)
ttfunk (1.7.0)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
asciidoctor (~> 2.0)
asciidoctor-pdf (~> 2.3)
rake (~> 13.3)
BUNDLED WITH
2.6.9

View File

@@ -4,16 +4,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'open3' require 'open3'
require 'pathname'
require 'rake/clean' require 'rake/clean'
require 'asciidoctor-pdf'
STAGES = Dir.glob('boot/stage*') STAGES = Dir.glob('boot/stage*')
.collect { |stage| File.basename stage } .collect { |stage| File.basename stage }
.sort { |a, b| a.delete_prefix('stage').to_i <=> b.delete_prefix('stage').to_i } .sort { |a, b| a.delete_prefix('stage').to_i <=> b.delete_prefix('stage').to_i }
.drop(1) # First assembly stage does not count. .drop(1) # First assembly stage does not count.
CLEAN.include 'doc/*.pdf'
CLOBBER.include 'build' CLOBBER.include 'build'
def run(exe) def run(exe)
@@ -49,10 +46,3 @@ task :convert do
end end
end end
end end
rule '.pdf' => '.adoc' do |t|
Asciidoctor.convert_file t.source, backend: 'pdf', safe: :safe
end
desc 'Generate documentation'
task doc: 'doc/language.pdf'

View File

@@ -1,307 +0,0 @@
= The programming language Elna
:toc:
== Introduction
Elna is a simple, imperative, low-level programming language.
It is intendet to accompany other languages in the areas, where a high-level
language doesn't fit well. It is also supposed to be an intermediate
representation for a such high-level hypothetical programming language.
== Expressions
The expression `@r.field` includes 3 expressions:
1. Variable expression `r`.
2. Field access `(r).field`.
3. Address taking `@(r.field)`.
The expression is evaluated in the above order. The postfix expressions
like field access have higher precedence than prefix operators.
=== Binary expressions
.Operator precedence
|===
|Precedence |Operator |Description
|1 |* / % |Multiplication, division and remainder.
|2 |+ - |Addition and subtraction.
|3 |<< >> |Left and right shifts.
|4 |= <> > < <= >=| Relational operators.
|5 |or xor &| Logical operators.
|===
=== Unary expressions
Unary expressions are expressions with a prefix operator followed by one
operand.
`@` (*at sign*) takes the address of its operand. The operand expression should be
addressable.
`-` (*minus*) negates a numeric value.
`~` (*tilde*) applied on a boolean acts as logical not. Applied on a numeric as
bitwise not.
== Conditional statements
== Loop statements
== Type system
```
type = array-type | pointer-type | record-type | enumeration-type
| procedure-type | identifier.
```
=== Primitive types
* Pointer
* Word
* Int
* Bool
* String
* Char
=== Pointer types
```
pointer-type = "^" type.
```
Example:
```
program;
var
x: Int;
y: ^Int;
begin
y := @x;
y^ := 0
end.
```
=== Static array
```
array-type = "[" expression "]" type.
```
Example:
```
program;
var
array: [3]Int := [1, 2, 3];
begin
array[1] := array[2]
end.
```
=== Procedure types
```
procedure-heading = "proc" identifier-definition
"(" [field {"," field}] ")" return-declaration.
block = constant-part variable-part statement-part "end".
procedure-declaration = procedure-heading ";" (block | "extern").
return-declaration = ["->" "!" | "->" type].
procedure-type = "proc" "(" [types] ")" return-declaration.
```
Example:
```
program;
var
a: proc(Int) -> Int;
proc f(x: Int) -> Int;
end;
begin
a := f;
a(0)
end.
```
=== Records
```
field = identifier ":" type.
record-type = "record" ["(" identifier ")"] [field {";" field}] "end".
```
Example:
```
program;
type
T = record
x: Int
end;
U = record(T)
y: Int;
z: Int
end;
var
u: U;
begin
u := U(0, 1, 2);
u.x := 3
end.
```
=== Enumerations
```
enumeration-type = "(" identifier {"," identifier} ")".
```
Example:
```
program;
type
E = (one, two, three);
var
e: E;
begin
e := E.one
end.
```
=== Type operations
=== Cast
```
cast = "cast" "(" expression ":" type ")".
```
The type of an object can be reinterpreted with a cast expression:
`cast(object: Type)`.
=== Traits
```
trait-identifier = "#" identifier.
trait = trait-identifier "(" [types] ")".
```
Traits allow to query some information about the types, like their size or
field offset or alignment. Calling a trait looks like a procedure call but
traits names start with a `#` and their arguments are type expressions and
not value expressions.
Supported compiler traits:
- `#size(T)` queries type size.
- `#align(T)` queries type alignment.
- `#offset(T, F)` queries the offset of the field `F` in the record `T`.
== Appendix
=== Syntax
```
letter = "A" | "B" | … | "Z" | "a" | "b" | … | "z" | "_".
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
character = ? a printable character ?.
hex-digit = digit | "A" | "B" | … | "F" | "a" | "b" | … | "f".
binary-digit = "0" | "1".
identifier = letter {letter | digit}.
identifier-definition = identifier ["*"].
trait-identifier = "#" identifier.
integer-literal = digit {digit}.
word-literal = integer-literal "u"
| "0" ("X" | "x") hex-digit {hex-digit}
| "0" ("B" | "b") binary-digit {binary-digit}.
real-literal = digit {digit} "." digit {digit}.
string-literal = """ {character} """.
character-literal = "'" {character} "'".
literal = integer-literal | word-literal | real-literal
| string-literal | character-literal
| "true" | "false" | "nil".
trait = trait-identifier "(" [types] ")".
cast = "cast" "(" expression ":" type ")".
procedure-call = designator "(" [expressions] ")".
relation-operator = "=" | "<>" | "<" | ">" | "<=" | ">=".
multiplication-operator = "*" | "/" | "%".
addition-operator = "+" | "-".
shift-operator = "<<" | ">>".
unary-operator = "@" | "~" | "-".
selector = "[" expression "]" | "." identifier | "^".
case = expressions ":" optional-statements.
designator = reference selector | identifier.
reference = literal | designator | trait | cast | procedure-call
| "(" expression ")".
factor = unary-operator factor | reference.
term = factor {multiplication-operator factor}.
simple-expression = term {addition-operator term}.
comparand = simple-expression {shift-operator simple-expression}.
relation = comparand {relation-operator comparand}.
operand = relation {"&" relation}.
expression = operand {("or" | "xor") operand}.
expressions = expression {"," expression}.
identifier-definitions = identifier-definition {"," identifier-definition}.
types = type {"," type}.
required-statements = statement {";" statement}.
optional-statements = [required-statements].
return-declaration = ["->" "!" | "->" type].
field = identifier ":" type.
array-type = "[" expression "]" type.
pointer-type = "^" type.
record-type = "record" ["(" identifier ")"] [field {";" field}] "end".
enumeration-type = "(" identifier {"," identifier} ")".
procedure-type = "proc" "(" [types] ")" return-declaration.
type = array-type | pointer-type | record-type | enumeration-type
| procedure-type | identifier.
assignment = designator ":=" expression.
if-statement = "if" expression "then" optional-statements
{"elsif" expression "then" optional-statements}
["else" optional-statements] "end".
while-statement = "while" expression "do" optional-statements
{"elsif" expression "do" optional-statements} "end".
defer-statement = "defer" optional-statements "end".
case-statement = "case" expression "of" case {"|" case}
["else" optional-statements] "end".
label-declaration = "." identifier.
goto-statement = "goto" identifier.
statement = assignment | procedure-call | defer-statement
| label-declaration | goto-statement |
| while-statement | if-statement | case-statement.
statement-part = ["begin" required-statements | "return" expression
| "begin" required-statements ";" "return" expression].
constant-declaration = identifier-definition ":=" expression.
constant-part = ["const" {constant-declaration ";"}].
variable-declaration = identifier-definitions ":" type
[":=" (expression | "extern")].
variable-part = ["var" {variable-declaration ";"}].
type_declaration = identifier-definition "=" type.
type-part ["type" {type-declaration ";"}].
import-declaration = identifier {"." identifier}.
import-part = ["import" {import_declarations ";"}].
procedure-heading = "proc" identifier-definition
"(" [field {"," field}] ")" return-declaration.
block = constant-part variable-part statement-part "end".
procedure-declaration = procedure-heading ";" (block | "extern").
declaration-sequence = import-part
constant-part type-part variable-part {procedure-declaration ";"}.
program = "program" ";" declaration-sequence statement-part "end" "."
| "module" ";" declaration-sequence "end" "."
```

418
doc/language.tex Normal file
View File

@@ -0,0 +1,418 @@
\documentclass{scrreprt}
\usepackage[T1]{fontenc}
\usepackage{tabularx}
\usepackage{booktabs}
\usepackage{listings}
\usepackage{syntax}
\title{The programming language Elna}
\begin{document}
\maketitle
\tableofcontents
\chapter{Introduction}
Elna is a simple, imperative, low-level programming language.
It is intendet to accompany other languages in the areas, where a high-level
language doesn't fit well. It is also supposed to be an intermediate
representation for a such high-level hypothetical programming language.
\part{Language}
\chapter{Expressions}
The expression \verb|@r.field| includes 3 expressions:
\begin{enumerate}
\item Variable expression \verb|r|.
\item Field access \verb|(r).field|.
\item Address taking \verb|@(r.field)|.
\end{enumerate}
The expression is evaluated in the above order. The postfix expressions
like field access have higher precedence than prefix operators.
\section{Binary expressions}
\begin{table}
\centering
\begin{tabularx}{0.8\textwidth}{%
l
>{\centering\arraybackslash}X
>{\raggedright\arraybackslash}X
}
\textbf{Precedence} & \textbf{Operator} & \textbf{Description}\\
\toprule
1 & $* \quad / \quad \%$ & Multiplication, division and remainder.\\
\midrule
2 & $+ \quad -$ & Addition and subtraction.\\
\midrule
3 & $<< \quad >>$ & Left and right shifts.\\
\midrule
4 & $= \quad <> \quad > \quad < \quad <= \quad >=$ & Relational operators.\\
\midrule
5 & $or \quad xor \quad \&$ & Logical operators.\\
\bottomrule
\end{tabularx}
\caption{Operator precedence}
\end{table}
\section{Unary expressions}
Unary expressions are expressions with a prefix operator followed by one
operand.
\verb|@| (\textit{at sign}) takes the address of its operand. The operand expression should be
addressable.
\verb|-| (\textit{minus}) negates a numeric value.
\verb|~| (\textit{tilde}) applied on a boolean acts as logical not. Applied on a numeric as
bitwise not.
\chapter{Conditional statements}
\chapter{Loop statements}
\part{Type system}
\begin{grammar}
<type> = <array-type>
\alt{} <pointer-type>
\alt{} <record-type>
\alt{} <enumeration-type>
\alt{} <procedure-type>
\alt{} <identifier>.
\end{grammar}
\chapter{Primitive types}
\begin{itemize}
\item Pointer
\item Word
\item Int
\item Bool
\item String
\item Char
\end{itemize}
\chapter{Pointer types}
\begin{grammar}
<pointer-type> = `^' <type>.
\end{grammar}
\begin{lstlisting}[caption=Example]
program;
var
x: Int;
y: ^Int;
begin
y := @x;
y^ := 0
end.
\end{lstlisting}
\chapter{Static array}
\begin{grammar}
<array-type> = `[' <expression> `]' <type>.
\end{grammar}
\begin{lstlisting}[caption=Example]
program;
var
array: [3]Int := [1, 2, 3];
begin
array[1] := array[2]
end.
\end{lstlisting}
\chapter{Procedure types}
\begin{grammar}
<procedure-heading> = `proc' <identifier-definition> \\
`(' [<field> \{`,' <field>\}] `)' <return-declaration>.
<block> = <constant-part> <variable-part> <statement-part> `end'.
<procedure-declaration> = <procedure-heading> `;' (block | `extern').
<return-declaration> = [`->' `!\@' | `->' type].
<procedure-type> = `proc' `(' [<types>] `)' <return-declaration>.
\end{grammar}
\begin{lstlisting}[caption=Example]
program;
var
a: proc(Int) -> Int;
proc f(x: Int) -> Int;
end;
begin
a := f;
a(0)
end.
\end{lstlisting}
\chapter{Records}
\begin{grammar}
<field> = <identifier> `:\@' <type>.
<record-type> = `record' [`(' <identifier> `)'] [<field> \{`;' <field>\}] `end'.
\end{grammar}
\begin{lstlisting}[caption=Example]
program;
type
T = record
x: Int
end;
U = record(T)
y: Int;
z: Int
end;
var
u: U;
begin
u := U(0, 1, 2);
u.x := 3
end.
\end{lstlisting}
\chapter{Enumerations}
\begin{grammar}
<enumeration-type> = `(' <identifier> \{`,' <identifier>\} `)'.
\end{grammar}
\begin{lstlisting}[caption=Example]
program;
type
E = (one, two, three);
var
e: E;
begin
e := E.one
end.
\end{lstlisting}
\chapter{Type operations}
\chapter{Cast}
\begin{grammar}
<cast> = `cast' `(' <expression> `:\@' <type> `)'.
\end{grammar}
The type of an object can be reinterpreted with a cast expression: \\
\verb|cast(object: Type)|.
\chapter{Traits}
\begin{grammar}
<trait-identifier> = `#' <identifier>.
<trait> = <trait-identifier> `(' [<types>] `)'.
\end{grammar}
Traits allow to query some information about the types, like their size or
field offset or alignment. Calling a trait looks like a procedure call but
traits names start with a \verb|#| and their arguments are type expressions and
not value expressions.
Supported compiler traits:
\begin{itemize}
\item \verb|#size(T)| queries type size.
\item \verb|#align(T)| queries type alignment.
\item \verb|#offset(T, F)| queries the offset of the field \verb|F| in the record \verb|T|.
\end{itemize}
\part{Appendix}
\chapter{Syntax}
\begin{grammar}
<letter> = `A' | `B' | … | `Z' | `a' | `b' | … | `z' | `\_'.
<counting-digit> = `1' | `2' | `3' | `4' | `5' | `6' | `7' | `8' | `9'.
<decimal-digit> = `0' | <counting-digit>.
<hex-digit> = <decimal-digit> | `A' | `B' | … | `F' | `a' | `b' | … | `f'.
<binary-digit> = `0' | `1'.
<hex-character> = `\\x' <hex-digit> <hex-digit>.
<escaped-character> = `\\' \\
(`n' | `a' | `b' | `t' | `f' | `r' | `v' | `\\' | `\textquotesingle' | `\textquotedbl' | `?\@' | `0').
<printable-character> = \enspace? a printable ASCII character\space?.
<character> = <printable-character> | <escaped-character> | <hex-digit>.
<identifier> = <letter> \{<letter> | <decimal-digit>\}.
<identifier-definition> = <identifier> [`*'].
<trait-identifier> = `#' <identifier>.
<integer-literal> = `0' | <counting-digit> \{<decimal-digit>\}.
<word-literal> = <integer-literal> `u'
\alt{} `0' (`X' | `x') <hex-digit> \{<hex-digit>\}
\alt{} `0' (`B' | `b') <binary-digit> \{<binary-digit>\}.
<real-literal> = <integer-literal> `.\@' <decimal-digit> \{<decimal-digit>\}
\alt{} <integer-literal>\} `e' [`+' | `-'] <decimal-digit> \{<decimal-digit>\}.
<string-literal> = `\textquotedbl' \{<character>\} `\textquotedbl'.
<character-literal> = `\textquotesingle' <character> `\textquotesingle'.
<literal> = <integer-literal> | <word-literal> | <real-literal>
\alt{} <string-literal> | <character-literal>
\alt{} `true' | `false' | `nil'.
<trait> = <trait-identifier> `(' [<types>] `)'.
<cast> = `cast' `(' <expression> `:\@' <type> `)'.
<procedure-call> = <designator> `(' [<expressions>] `)'.
<relation-operator> = `=' | `<>' | `<' | `>' | `<=' | `>='.
<multiplication-operator> = `*' | `/' | `\%'.
<addition-operator> = `+' | `-'.
<shift-operator> = `<<' | `>>'.
<unary-operator> = `@' | `~' | `-'.
<selector> = `[' <expression> `]' | `.\@' <identifier> | `^'.
<case> = <expressions> `:\@' <optional-statements>.
<designator> = <reference> <selector> | <identifier>.
<reference> = <literal>
\alt{} <designator>
\alt{} <trait>
\alt{} <cast>
\alt{} <procedure-call>
\alt{} `(' <expression> `)'.
<factor> = <unary-operator> <factor> | <reference>.
<term> = <factor> \{<multiplication-operator> <factor>\}.
<simple-expression> = <term> \{<addition-operator> <term>\}.
<comparand> = <simple-expression> \{<shift-operator> <simple-expression>\}.
<relation> = <comparand> \{<relation-operator> <comparand>\}.
<operand> = <relation> \{`&' <relation>\}.
<expression> = <operand> \{(`or' | `xor') <operand>\}.
<expressions> = <expression> \{`,' <expression>\}.
<identifier-definitions> = <identifier-definition> \{`,' <identifier-definition>\}.
<types> = <type> \{`,' <type>\}.
<required-statements> = <statement> \{`;' <statement>\}.
<optional-statements> = [<required-statements>].
<return-declaration> = [`->' `!\@' | `->' type].
<field> = <identifier> `:\@' <type>.
<array-type> = `[' <expression> `]' <type>.
<pointer-type> = `^' <type>.
<record-type> = `record' [`(' <identifier> `)'] [<field> \{`;' <field>\}] `end'.
<enumeration-type> = `(' <identifier> \{`,' <identifier>\} `)'.
<procedure-type> = `proc' `(' [<types>] `)' <return-declaration>.
<type> = <array-type>
\alt{} <pointer-type>
\alt{} <record-type>
\alt{} <enumeration-type>
\alt{} <procedure-type>
\alt{} <identifier>.
<assignment> = <designator> `:=' <expression>.
<if-statement> = `if' <expression> `then' <optional-statements> \\
\{`elsif' <expression> `then' <optional-statements>\} \\
{[`else' <optional-statements>]} `end'.
<while-statement> = `while' <expression> `do' <optional-statements> \\
\{`elsif' <expression> `do' <optional-statements>\} `end'.
<defer-statement> = `defer' <optional-statements> `end'.
<case-statement> = `case' <expression> `of' <case> \{`|' case\} \\
{[`else' <optional-statements>]} `end'.
<label-declaration> = `.\@' <identifier>.
<goto-statement> = `goto' <identifier>.
<statement> = <assignment> | <procedure-call> | <defer-statement>
| <label-declaration> | <goto-statement> |
| <while-statement> | <if-statement> | <case-statement>.
<statement-part> = [`begin' <required-statements>
\alt{} `return' <expression>
\alt{} `begin' <required-statements> `;' `return' <expression>].
<constant-declaration> = <identifier-definition> `:=' <expression>.
<constant-part> = [`const' \{<constant-declaration> `;'\}].
<variable-declaration> = <identifier-definitions> `:\@' <type> \\
{[`:=' (<expression> | `extern')]}.
<variable-part> = [`var' \{<variable-declaration> `;'\}].
<type-declaration> = <identifier-definition> `=' <type>.
<type-part> = [`type' \{<type-declaration> `;'\}].
<import-declaration> = <identifier> \{`.\@' <identifier>\}.
<import-part> = [`import' \{import-declaration `;'\}].
<procedure-heading> = `proc' <identifier-definition> \\
`(' [<field> \{`,' <field>\}] `)' <return-declaration>.
<block> = <constant-part> <variable-part> <statement-part> `end'.
<procedure-declaration> = <procedure-heading> `;' (block | `extern').
<declaration-sequence> = <import-part> \\
<constant-part> <type-part> <variable-part> \\
\{<procedure-declaration> `;'\}.
<program> = `program' `;' <declaration-sequence> <statement-part> `end' `.\@'
\alt{} `module' `;' <declaration-sequence> `end' `.\@'.
\end{grammar}
\end{document}

23
rakelib/doc.rake Normal file
View File

@@ -0,0 +1,23 @@
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
# frozen_string_literal: true
require 'pathname'
require 'rake/clean'
CLEAN.include 'build/doc'
directory 'build/doc'
rule /build\/doc\/\w+.pdf/ => lambda { |t|
[
Pathname.new(t).relative_path_from('build').sub_ext('.tex').to_path,
'build/doc'
]
} do |t|
sh 'pdflatex', '--output-directory', 'build/doc', t.prerequisites.first
end
desc 'Generate documentation'
task doc: 'build/doc/language.pdf'

View File

@@ -3,6 +3,7 @@
# obtain one at https://mozilla.org/MPL/2.0/. # obtain one at https://mozilla.org/MPL/2.0/.
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname'
require 'rake/clean' require 'rake/clean'
CLEAN.include 'build/boot', 'build/valid' CLEAN.include 'build/boot', 'build/valid'