diff --git a/.gitignore b/.gitignore index 07f3f91..9a672ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ /build/ /vendor/ -/doc/*.html -/doc/*.pdf +*.pdf +*.log +*.aux +*.toc a.out diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 40d0a36..0000000 --- a/Gemfile +++ /dev/null @@ -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" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 59da3dc..0000000 --- a/Gemfile.lock +++ /dev/null @@ -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 diff --git a/Rakefile b/Rakefile index d12b9a7..69a36c6 100644 --- a/Rakefile +++ b/Rakefile @@ -4,16 +4,13 @@ # frozen_string_literal: true require 'open3' -require 'pathname' require 'rake/clean' -require 'asciidoctor-pdf' STAGES = Dir.glob('boot/stage*') .collect { |stage| File.basename stage } .sort { |a, b| a.delete_prefix('stage').to_i <=> b.delete_prefix('stage').to_i } .drop(1) # First assembly stage does not count. -CLEAN.include 'doc/*.pdf' CLOBBER.include 'build' def run(exe) @@ -49,10 +46,3 @@ task :convert do 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' diff --git a/doc/language.adoc b/doc/language.adoc deleted file mode 100644 index e6c5e7b..0000000 --- a/doc/language.adoc +++ /dev/null @@ -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" "." -``` diff --git a/doc/language.tex b/doc/language.tex new file mode 100644 index 0000000..bfa21cb --- /dev/null +++ b/doc/language.tex @@ -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} + = + \alt{} + \alt{} + \alt{} + \alt{} + \alt{} . +\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} + = `^' . +\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} + = `[' `]' . +\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} + = `proc' \\ + `(' [ \{`,' \}] `)' . + + = `end'. + + = `;' (block | `extern'). + + = [`->' `!\@' | `->' type]. + + = `proc' `(' [] `)' . +\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} + = `:\@' . + + = `record' [`(' `)'] [ \{`;' \}] `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} + = `(' \{`,' \} `)'. +\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' `(' `:\@' `)'. +\end{grammar} + +The type of an object can be reinterpreted with a cast expression: \\ +\verb|cast(object: Type)|. + +\chapter{Traits} + +\begin{grammar} + = `#' . + + = `(' [] `)'. +\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} + = `A' | `B' | … | `Z' | `a' | `b' | … | `z' | `\_'. + + = `1' | `2' | `3' | `4' | `5' | `6' | `7' | `8' | `9'. + + = `0' | . + + = | `A' | `B' | … | `F' | `a' | `b' | … | `f'. + + = `0' | `1'. + + = `\\x' . + + = `\\' \\ + (`n' | `a' | `b' | `t' | `f' | `r' | `v' | `\\' | `\textquotesingle' | `\textquotedbl' | `?\@' | `0'). + + = \enspace? a printable ASCII character\space?. + + = | | . + + = \{ | \}. + + = [`*']. + + = `#' . + + = `0' | \{\}. + + = `u' + \alt{} `0' (`X' | `x') \{\} + \alt{} `0' (`B' | `b') \{\}. + + = `.\@' \{\} + \alt{} \} `e' [`+' | `-'] \{\}. + + = `\textquotedbl' \{\} `\textquotedbl'. + + = `\textquotesingle' `\textquotesingle'. + + = | | + \alt{} | + \alt{} `true' | `false' | `nil'. + + = `(' [] `)'. + + = `cast' `(' `:\@' `)'. + + = `(' [] `)'. + + = `=' | `<>' | `<' | `>' | `<=' | `>='. + + = `*' | `/' | `\%'. + + = `+' | `-'. + + = `<<' | `>>'. + + = `@' | `~' | `-'. + + = `[' `]' | `.\@' | `^'. + + = `:\@' . + + = | . + + = + \alt{} + \alt{} + \alt{} + \alt{} + \alt{} `(' `)'. + + = | . + + = \{ \}. + + = \{ \}. + + = \{ \}. + + = \{ \}. + + = \{`&' \}. + + = \{(`or' | `xor') \}. + + = \{`,' \}. + + = \{`,' \}. + + = \{`,' \}. + + = \{`;' \}. + + = []. + + = [`->' `!\@' | `->' type]. + + = `:\@' . + + = `[' `]' . + + = `^' . + + = `record' [`(' `)'] [ \{`;' \}] `end'. + + = `(' \{`,' \} `)'. + + = `proc' `(' [] `)' . + + = + \alt{} + \alt{} + \alt{} + \alt{} + \alt{} . + + = `:=' . + + = `if' `then' \\ + \{`elsif' `then' \} \\ + {[`else' ]} `end'. + + = `while' `do' \\ + \{`elsif' `do' \} `end'. + + = `defer' `end'. + + = `case' `of' \{`|' case\} \\ + {[`else' ]} `end'. + + = `.\@' . + + = `goto' . + + = | | + | | | + | | | . + + = [`begin' + \alt{} `return' + \alt{} `begin' `;' `return' ]. + + = `:=' . + + = [`const' \{ `;'\}]. + + = `:\@' \\ + {[`:=' ( | `extern')]}. + + = [`var' \{ `;'\}]. + + = `=' . + + = [`type' \{ `;'\}]. + + = \{`.\@' \}. + + = [`import' \{import-declaration `;'\}]. + + = `proc' \\ + `(' [ \{`,' \}] `)' . + + = `end'. + + = `;' (block | `extern'). + + = \\ + \\ + \{ `;'\}. + + = `program' `;' `end' `.\@' + \alt{} `module' `;' `end' `.\@'. +\end{grammar} + +\end{document} diff --git a/rakelib/doc.rake b/rakelib/doc.rake new file mode 100644 index 0000000..d719c80 --- /dev/null +++ b/rakelib/doc.rake @@ -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' diff --git a/rakelib/ninja.rake b/rakelib/ninja.rake index 702f9b5..de213ef 100644 --- a/rakelib/ninja.rake +++ b/rakelib/ninja.rake @@ -3,6 +3,7 @@ # obtain one at https://mozilla.org/MPL/2.0/. # frozen_string_literal: true +require 'pathname' require 'rake/clean' CLEAN.include 'build/boot', 'build/valid'