aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/driver.cc2
-rw-r--r--boot/name_analysis.cc50
-rw-r--r--boot/symbol.cc15
-rw-r--r--boot/type_check.cc8
-rw-r--r--include/elna/boot/name_analysis.h10
-rw-r--r--include/elna/boot/symbol.h10
-rw-r--r--source/elna/lexer.elna608
-rw-r--r--source/elna/source.elna20
-rw-r--r--source/main.elna3
-rw-r--r--source/std.elna6
-rw-r--r--testsuite/fail_compilation/array_dimension_size.elna8
-rw-r--r--testsuite/fail_compilation/array_dimension_type.elna10
-rw-r--r--testsuite/fail_compilation/array_index_type.elna11
-rw-r--r--testsuite/fail_compilation/constant_enum_to_int.elna2
-rw-r--r--testsuite/runnable/backquote_escape.elna4
15 files changed, 757 insertions, 10 deletions
diff --git a/boot/driver.cc b/boot/driver.cc
index 14575f7..be52f23 100644
--- a/boot/driver.cc
+++ b/boot/driver.cc
@@ -66,6 +66,8 @@ namespace elna::boot
return '\'';
case '"':
return '"';
+ case '`':
+ return '`';
case '0':
return '\0';
default:
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 36faee1..9305cd2 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -144,6 +144,11 @@ namespace elna::boot
{
return "All constants should be initialized";
}
+ else if constexpr (std::is_same_v<T, array_dimension_type>)
+ {
+ return "Array dimension must be of type 'Word' or 'Int', but got '"
+ + payload.actual.to_string() + "'";
+ }
else if constexpr (std::is_same_v<T, kind>)
{
switch (payload)
@@ -162,6 +167,8 @@ namespace elna::boot
case enumeration_values:
return "Enumeration values should be omitted, given for the first member only,"
" or given for all members";
+ case array_dimension_size:
+ return "Array dimension must be greater than zero";
default:
__builtin_unreachable();
}
@@ -327,7 +334,7 @@ namespace elna::boot
resolving_visitor::resolving_visitor(symbol_bag& bag, const target_info& target,
const std::filesystem::path& module_path)
- : bag(bag), constant_evaluator(this->bag, target), module_file(module_path)
+ : bag(bag), target(target), constant_evaluator(this->bag, target), module_file(module_path)
{
}
@@ -580,15 +587,52 @@ namespace elna::boot
this->current_type = type();
return;
}
- const auto size_constant = this->constant_evaluator.evaluate_index(expression->dimensions());
- if (!size_constant.has_value())
+ if (!is_index_type(expression->dimensions().type_decoration, this->target))
+ {
+ add_error<declaration_format_error>(expression->dimensions().position(),
+ declaration_format_error::array_dimension_type{ expression->dimensions().type_decoration });
+ this->current_type = type();
+ return;
+ }
+
+ const std::optional<constant_value> evaluated =
+ this->constant_evaluator.evaluate(expression->dimensions());
+
+ if (!evaluated.has_value())
+ {
+ add_error<non_constant_expression_error>(expression->position(),
+ non_constant_expression_error::array_dimensions{ array_base });
+ this->current_type = type();
+ return;
+ }
+
+ const auto *literal = std::get_if<integer_literal>(&evaluated.value());
+
+ if (literal == nullptr)
{
add_error<non_constant_expression_error>(expression->position(),
non_constant_expression_error::array_dimensions{ array_base });
this->current_type = type();
return;
}
+ if (*literal <= 0)
+ {
+ add_error<declaration_format_error>(expression->dimensions().position(),
+ declaration_format_error::kind::array_dimension_size);
+ this->current_type = type();
+ return;
+ }
+
+ const std::optional<std::uint64_t> size_constant = literal->try_to<std::uint64_t>();
+
+ if (!size_constant.has_value())
+ {
+ add_error<declaration_format_error>(expression->dimensions().position(),
+ declaration_format_error::kind::array_dimension_size);
+ this->current_type = type();
+ return;
+ }
this->current_type = type(std::make_shared<array_type>(array_base, size_constant.value()));
}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 8feba4a..310ae8a 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -830,6 +830,21 @@ namespace elna::boot
|| is_float_type(checked);
}
+ bool is_index_type(const type& checked, const target_info& target)
+ {
+ const type unaliased = resolve_underlying_type(checked);
+ if (const auto primitive = unaliased.get<primitive_type>())
+ {
+ const bool is_int = primitive->identifier.starts_with("Int")
+ && primitive->properties.size == target.int_properties.front().size;
+ const bool is_word = primitive->identifier.starts_with("Word")
+ && primitive->properties.size == target.word_properties.front().size;
+
+ return is_int || is_word;
+ }
+ return false;
+ }
+
bool is_integral_type(const type& checked)
{
return is_integer_type(checked) || is_word_type(checked);
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 797282c..5a8170a 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -250,7 +250,7 @@ namespace elna::boot
return "Condition must be a boolean expression, but got '"
+ this->actual.to_string() + "'";
case array_index:
- return "Array index must be an integral type, but got '"
+ return "Array index must be of type 'Word' or 'Int', but got '"
+ this->actual.to_string() + "'";
case non_indexable:
return "Indexing is not allowed on type '"
@@ -1528,12 +1528,12 @@ namespace elna::boot
{
walking_visitor::visit(expression);
- if (!is_integral_type(resolve_underlying_type(expression->start().type_decoration)))
+ if (!is_index_type(expression->start().type_decoration, this->target))
{
add_error<type_requirement_error>(expression->start().position(),
expression->start().type_decoration, type_requirement_error::kind::array_index);
}
- if (!is_integral_type(resolve_underlying_type(expression->end().type_decoration)))
+ if (!is_index_type(expression->end().type_decoration, this->target))
{
add_error<type_requirement_error>(expression->end().position(),
expression->end().type_decoration, type_requirement_error::kind::array_index);
@@ -1551,7 +1551,7 @@ namespace elna::boot
add_error<type_requirement_error>(expression->position(),
expression->base().type_decoration, type_requirement_error::kind::non_indexable);
}
- if (!is_integral_type(resolve_underlying_type(expression->index().type_decoration)))
+ if (!is_index_type(expression->index().type_decoration, this->target))
{
add_error<type_requirement_error>(expression->index().position(),
expression->index().type_decoration, type_requirement_error::kind::array_index);
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index 1d9676a..3363fe5 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -133,13 +133,18 @@ namespace elna::boot
constant_procedure,
constant_record_constructor,
module_entry,
- enumeration_values
+ enumeration_values,
+ array_dimension_size
};
struct not_initialized
{
std::vector<identifier> identifiers;
};
- using payload_type = std::variant<not_initialized, kind>;
+ struct array_dimension_type
+ {
+ type actual;
+ };
+ using payload_type = std::variant<not_initialized, array_dimension_type, kind>;
declaration_format_error(const source_position position, payload_type payload);
std::optional<diagnostic_note> note() const override;
@@ -235,6 +240,7 @@ namespace elna::boot
protected:
type current_type;
symbol_bag& bag;
+ const target_info& target;
evaluator constant_evaluator;
std::filesystem::path module_file;
/// Number of defer bodies the visited statement stands in.
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 190a297..c9ba88a 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -836,6 +836,16 @@ namespace elna::boot
bool is_numeric_type(const type& checked);
/**
+ * Checks whether the given type is suitable as an index or dimension
+ * (\c Word or \c Int).
+ *
+ * \param checked The type to check.
+ * \param target Target type information.
+ * \return Whether the type is an index type.
+ */
+ bool is_index_type(const type& checked, const target_info& target);
+
+ /**
* Checks whether the given type is an integral primitive type
* (\c Int or \c Word).
*
diff --git a/source/elna/lexer.elna b/source/elna/lexer.elna
new file mode 100644
index 0000000..7fa489a
--- /dev/null
+++ b/source/elna/lexer.elna
@@ -0,0 +1,608 @@
+(* 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/. *)
+
+(* Lexical analysis. *)
+module
+
+import std, elna.source
+
+type
+ (*
+ Classification table assigns each possible character to a group (class). All
+ characters of the same group are processed equivalently.
+ *)
+ ElnaLexerClass = (
+ (* Zero has its own class to represent prefixed numbers 0x or 0b. *)
+ zero,
+ (* Decimal digits without zero. *)
+ digit,
+ (* E and e. *)
+ exponent,
+ (* Letters without e and E (exponent) and underscore. *)
+ alpha,
+ (* Horizontal whitespace (space, tab and carriage return). *)
+ space,
+ (* Newline. *)
+ new_line,
+ (* Standalone or starts the assignment operator. *)
+ colon,
+ (* Standalone or part of comparison operators or the assignment operator. *)
+ equals,
+ (* Standalone or opens a comment. *)
+ left_paren,
+ (* Standalone or closes a comment. *)
+ right_paren,
+ (* Multiplication, export marker or in comment delimiters. *)
+ asterisk,
+ (* Escapes quotes in string and character literals. *)
+ backslash,
+ (* Standalone single-character tokens (e.g. ';', '^', '@') that never combine. *)
+ single,
+ (* UTF-32 string literal delimiter. *)
+ backquote,
+ (* Record initializer or attribute marker (#{). *)
+ brace,
+ (* Opening square bracket for array indices and generic parameters. *)
+ square,
+ (* Sentinel signaling end of input. *)
+ eof,
+ (* Standalone or as decimal separator. *)
+ dot,
+ (* Plus for positive exponents. *)
+ plus,
+ (* Standalone, arrow (->) or line comment (--). *)
+ minus,
+ (* ASCII character literal delimiter. *)
+ single_quote,
+ (* UTF-8 string literal delimiter. *)
+ double_quote,
+ (* Greater, greater than or an arrow. *)
+ greater,
+ (* Less, less than or unequal (<>). *)
+ less,
+ (* Attribute, generic parameter or trait marker ('#'). *)
+ number_sign,
+ (* Other valid symbols that do not form special tokens. *)
+ other,
+ (* Cannot appear in the source code. *)
+ invalid
+ )
+ (* Lexer state. *)
+ ElnaLexerState = (
+ (* A non-accepting state from which no final state can ever be reached. *)
+ dead,
+ (* Start state. *)
+ start,
+ (* Entering identifier or keyword. *)
+ identifier,
+ (* Skipping whitespaces. *)
+ skip,
+ (* Entering a token starting with a colon. *)
+ colon,
+ (* Entering a token starting with a left parenthesis. *)
+ left_paren,
+ (* Minus or token starting with a minus. *)
+ minus,
+ (* Entering comparison operator starting with "greater than". *)
+ greater,
+ (* Entering comparison operator starting with "less than". *)
+ less,
+ (* Binary or hexadecimal word. *)
+ leading_zero,
+ (* Entering integer part of a number. *)
+ integer,
+ (* Inside a comment (after eating double minus, --). *)
+ comment,
+ (* Inside a docblock (after eating opening paren with asterisk). *)
+ docblock,
+ (* Seen an asterisk inside a docblock. *)
+ closing_docblock,
+ (* Tail is the last part of the number containing digits and an optional type suffix. *)
+ number_tail,
+ (* Seen a period after a number, possible floating point number. *)
+ dot_after,
+ (* Entering floating point number fraction part after a decimal point. *)
+ fraction,
+ (* An exponent state begins with e or E. *)
+ exponent,
+ (* Optional plus or minus sign following the exponent. *)
+ exponent_sign,
+ (* Opening a compile-time feature starting with a #. *)
+ number_sign,
+ (* Trait identifier. *)
+ trait,
+ (* Entering UTF-32 character literal. *)
+ wide,
+ (* Seen an escape sequence in a UTF-32 character literal. *)
+ wide_escape,
+ (* Entering UTF-8 character literal. *)
+ narrow,
+ (* Seen an escape sequence in a UTF-8 character literal. *)
+ narrow_escape,
+ (* Entering string literal. *)
+ string,
+ (* Seen an escape sequence in a string literal. *)
+ string_escape,
+ (* Token is complete, the last byte eaten. *)
+ stop,
+ (* Reject the token with an error. *)
+ reject,
+ (* Sentinel signaling end of input. *)
+ eof
+ )
+
+var
+ (* Initializes the array with character classes. *)
+ classification: const [256]ElnaLexerClass := [
+ ElnaLexerClass.eof, -- nul
+ ElnaLexerClass.invalid, -- soh
+ ElnaLexerClass.invalid, -- stx
+ ElnaLexerClass.invalid, -- etx
+ ElnaLexerClass.invalid, -- eot
+ ElnaLexerClass.invalid, -- enq
+ ElnaLexerClass.invalid, -- ack
+ ElnaLexerClass.invalid, -- bel
+ ElnaLexerClass.invalid, -- bs
+ ElnaLexerClass.space, -- ht
+ ElnaLexerClass.new_line, -- nl
+ ElnaLexerClass.invalid, -- vt
+ ElnaLexerClass.invalid, -- np
+ ElnaLexerClass.space , -- cr
+ ElnaLexerClass.invalid, -- so
+ ElnaLexerClass.invalid, -- si
+ ElnaLexerClass.invalid, -- dle
+ ElnaLexerClass.invalid, -- dc1
+ ElnaLexerClass.invalid, -- dc2
+ ElnaLexerClass.invalid, -- dc3
+ ElnaLexerClass.invalid, -- dc4
+ ElnaLexerClass.invalid, -- nak
+ ElnaLexerClass.invalid, -- syn
+ ElnaLexerClass.invalid, -- etb
+ ElnaLexerClass.invalid, -- can
+ ElnaLexerClass.invalid, -- em
+ ElnaLexerClass.invalid, -- sub
+ ElnaLexerClass.invalid, -- esc
+ ElnaLexerClass.invalid, -- fs
+ ElnaLexerClass.invalid, -- gs
+ ElnaLexerClass.invalid, -- rs
+ ElnaLexerClass.invalid, -- us
+ ElnaLexerClass.space, -- Space
+ ElnaLexerClass.other, -- !
+ ElnaLexerClass.double_quote, -- "
+ ElnaLexerClass.number_sign, -- #
+ ElnaLexerClass.other, -- $
+ ElnaLexerClass.single, -- %
+ ElnaLexerClass.single, -- &
+ ElnaLexerClass.single_quote, -- '
+ ElnaLexerClass.left_paren, -- (
+ ElnaLexerClass.right_paren, -- )
+ ElnaLexerClass.asterisk, -- *
+ ElnaLexerClass.plus, -- +
+ ElnaLexerClass.single, -- ,
+ ElnaLexerClass.minus, -- -
+ ElnaLexerClass.dot, -- .
+ ElnaLexerClass.single, -- /
+ ElnaLexerClass.zero, -- 0
+ ElnaLexerClass.digit, -- 1
+ ElnaLexerClass.digit, -- 2
+ ElnaLexerClass.digit, -- 3
+ ElnaLexerClass.digit, -- 4
+ ElnaLexerClass.digit, -- 5
+ ElnaLexerClass.digit, -- 6
+ ElnaLexerClass.digit, -- 7
+ ElnaLexerClass.digit, -- 8
+ ElnaLexerClass.digit, -- 9
+ ElnaLexerClass.colon, -- :
+ ElnaLexerClass.single, -- ;
+ ElnaLexerClass.less, -- <
+ ElnaLexerClass.equals, -- =
+ ElnaLexerClass.greater, -- >
+ ElnaLexerClass.other, -- ?
+ ElnaLexerClass.single, -- @
+ ElnaLexerClass.alpha, -- A
+ ElnaLexerClass.alpha, -- B
+ ElnaLexerClass.alpha, -- C
+ ElnaLexerClass.alpha, -- D
+ ElnaLexerClass.exponent, -- E
+ ElnaLexerClass.alpha, -- F
+ ElnaLexerClass.alpha, -- G
+ ElnaLexerClass.alpha, -- H
+ ElnaLexerClass.alpha, -- I
+ ElnaLexerClass.alpha, -- J
+ ElnaLexerClass.alpha, -- K
+ ElnaLexerClass.alpha, -- L
+ ElnaLexerClass.alpha, -- M
+ ElnaLexerClass.alpha, -- N
+ ElnaLexerClass.alpha, -- O
+ ElnaLexerClass.alpha, -- P
+ ElnaLexerClass.alpha, -- Q
+ ElnaLexerClass.alpha, -- R
+ ElnaLexerClass.alpha, -- S
+ ElnaLexerClass.alpha, -- T
+ ElnaLexerClass.alpha, -- U
+ ElnaLexerClass.alpha, -- V
+ ElnaLexerClass.alpha, -- W
+ ElnaLexerClass.alpha , -- X
+ ElnaLexerClass.alpha, -- Y
+ ElnaLexerClass.alpha, -- Z
+ ElnaLexerClass.square, -- [
+ ElnaLexerClass.backslash, -- \
+ ElnaLexerClass.single, -- ]
+ ElnaLexerClass.single, -- ^
+ ElnaLexerClass.alpha, -- _
+ ElnaLexerClass.backquote, -- `
+ ElnaLexerClass.alpha , -- a
+ ElnaLexerClass.alpha , -- b
+ ElnaLexerClass.alpha , -- c
+ ElnaLexerClass.alpha , -- d
+ ElnaLexerClass.exponent, -- e
+ ElnaLexerClass.alpha , -- f
+ ElnaLexerClass.alpha, -- g
+ ElnaLexerClass.alpha, -- h
+ ElnaLexerClass.alpha, -- i
+ ElnaLexerClass.alpha, -- j
+ ElnaLexerClass.alpha, -- k
+ ElnaLexerClass.alpha, -- l
+ ElnaLexerClass.alpha, -- m
+ ElnaLexerClass.alpha, -- n
+ ElnaLexerClass.alpha, -- o
+ ElnaLexerClass.alpha, -- p
+ ElnaLexerClass.alpha, -- q
+ ElnaLexerClass.alpha, -- r
+ ElnaLexerClass.alpha, -- s
+ ElnaLexerClass.alpha, -- t
+ ElnaLexerClass.alpha, -- u
+ ElnaLexerClass.alpha, -- v
+ ElnaLexerClass.alpha, -- w
+ ElnaLexerClass.alpha , -- x
+ ElnaLexerClass.alpha, -- y
+ ElnaLexerClass.alpha, -- z
+ ElnaLexerClass.brace, -- {
+ ElnaLexerClass.single, -- |
+ ElnaLexerClass.single, -- }
+ ElnaLexerClass.single, -- ~
+ ElnaLexerClass.invalid, -- del
+ -- 0x80 .. 0xBF - UTF-8 continuation bytes (10xxxxxx).
+ ElnaLexerClass.other, -- 0x80
+ ElnaLexerClass.other, -- 0x81
+ ElnaLexerClass.other, -- 0x82
+ ElnaLexerClass.other, -- 0x83
+ ElnaLexerClass.other, -- 0x84
+ ElnaLexerClass.other, -- 0x85
+ ElnaLexerClass.other, -- 0x86
+ ElnaLexerClass.other, -- 0x87
+ ElnaLexerClass.other, -- 0x88
+ ElnaLexerClass.other, -- 0x89
+ ElnaLexerClass.other, -- 0x8A
+ ElnaLexerClass.other, -- 0x8B
+ ElnaLexerClass.other, -- 0x8C
+ ElnaLexerClass.other, -- 0x8D
+ ElnaLexerClass.other, -- 0x8E
+ ElnaLexerClass.other, -- 0x8F
+ ElnaLexerClass.other, -- 0x90
+ ElnaLexerClass.other, -- 0x91
+ ElnaLexerClass.other, -- 0x92
+ ElnaLexerClass.other, -- 0x93
+ ElnaLexerClass.other, -- 0x94
+ ElnaLexerClass.other, -- 0x95
+ ElnaLexerClass.other, -- 0x96
+ ElnaLexerClass.other, -- 0x97
+ ElnaLexerClass.other, -- 0x98
+ ElnaLexerClass.other, -- 0x99
+ ElnaLexerClass.other, -- 0x9A
+ ElnaLexerClass.other, -- 0x9B
+ ElnaLexerClass.other, -- 0x9C
+ ElnaLexerClass.other, -- 0x9D
+ ElnaLexerClass.other, -- 0x9E
+ ElnaLexerClass.other, -- 0x9F
+ ElnaLexerClass.other, -- 0xA0
+ ElnaLexerClass.other, -- 0xA1
+ ElnaLexerClass.other, -- 0xA2
+ ElnaLexerClass.other, -- 0xA3
+ ElnaLexerClass.other, -- 0xA4
+ ElnaLexerClass.other, -- 0xA5
+ ElnaLexerClass.other, -- 0xA6
+ ElnaLexerClass.other, -- 0xA7
+ ElnaLexerClass.other, -- 0xA8
+ ElnaLexerClass.other, -- 0xA9
+ ElnaLexerClass.other, -- 0xAA
+ ElnaLexerClass.other, -- 0xAB
+ ElnaLexerClass.other, -- 0xAC
+ ElnaLexerClass.other, -- 0xAD
+ ElnaLexerClass.other, -- 0xAE
+ ElnaLexerClass.other, -- 0xAF
+ ElnaLexerClass.other, -- 0xB0
+ ElnaLexerClass.other, -- 0xB1
+ ElnaLexerClass.other, -- 0xB2
+ ElnaLexerClass.other, -- 0xB3
+ ElnaLexerClass.other, -- 0xB4
+ ElnaLexerClass.other, -- 0xB5
+ ElnaLexerClass.other, -- 0xB6
+ ElnaLexerClass.other, -- 0xB7
+ ElnaLexerClass.other, -- 0xB8
+ ElnaLexerClass.other, -- 0xB9
+ ElnaLexerClass.other, -- 0xBA
+ ElnaLexerClass.other, -- 0xBB
+ ElnaLexerClass.other, -- 0xBC
+ ElnaLexerClass.other, -- 0xBD
+ ElnaLexerClass.other, -- 0xBE
+ ElnaLexerClass.other, -- 0xBF
+ ElnaLexerClass.invalid, -- 0xC0
+ ElnaLexerClass.invalid, -- 0xC1
+ -- 0xC2 .. 0xDF - 2-byte lead (110xxxxx).
+ ElnaLexerClass.other, -- 0xC2
+ ElnaLexerClass.other, -- 0xC3
+ ElnaLexerClass.other, -- 0xC4
+ ElnaLexerClass.other, -- 0xC5
+ ElnaLexerClass.other, -- 0xC6
+ ElnaLexerClass.other, -- 0xC7
+ ElnaLexerClass.other, -- 0xC8
+ ElnaLexerClass.other, -- 0xC9
+ ElnaLexerClass.other, -- 0xCA
+ ElnaLexerClass.other, -- 0xCB
+ ElnaLexerClass.other, -- 0xCC
+ ElnaLexerClass.other, -- 0xCD
+ ElnaLexerClass.other, -- 0xCE
+ ElnaLexerClass.other, -- 0xCF
+ ElnaLexerClass.other, -- 0xD0
+ ElnaLexerClass.other, -- 0xD1
+ ElnaLexerClass.other, -- 0xD2
+ ElnaLexerClass.other, -- 0xD3
+ ElnaLexerClass.other, -- 0xD4
+ ElnaLexerClass.other, -- 0xD5
+ ElnaLexerClass.other, -- 0xD6
+ ElnaLexerClass.other, -- 0xD7
+ ElnaLexerClass.other, -- 0xD8
+ ElnaLexerClass.other, -- 0xD9
+ ElnaLexerClass.other, -- 0xDA
+ ElnaLexerClass.other, -- 0xDB
+ ElnaLexerClass.other, -- 0xDC
+ ElnaLexerClass.other, -- 0xDD
+ ElnaLexerClass.other, -- 0xDE
+ ElnaLexerClass.other, -- 0xDF
+ -- 0xE0 .. 0xEF - 3-byte lead (1110xxxx).
+ ElnaLexerClass.other, -- 0xE0
+ ElnaLexerClass.other, -- 0xE1
+ ElnaLexerClass.other, -- 0xE2
+ ElnaLexerClass.other, -- 0xE3
+ ElnaLexerClass.other, -- 0xE4
+ ElnaLexerClass.other, -- 0xE5
+ ElnaLexerClass.other, -- 0xE6
+ ElnaLexerClass.other, -- 0xE7
+ ElnaLexerClass.other, -- 0xE8
+ ElnaLexerClass.other, -- 0xE9
+ ElnaLexerClass.other, -- 0xEA
+ ElnaLexerClass.other, -- 0xEB
+ ElnaLexerClass.other, -- 0xEC
+ ElnaLexerClass.other, -- 0xED
+ ElnaLexerClass.other, -- 0xEE
+ ElnaLexerClass.other, -- 0xEF
+ -- 0xF0 .. 0xF4 - 4-byte lead (11110xxx).
+ ElnaLexerClass.other, -- 0xF0
+ ElnaLexerClass.other, -- 0xF1
+ ElnaLexerClass.other, -- 0xF2
+ ElnaLexerClass.other, -- 0xF3
+ ElnaLexerClass.other, -- 0xF4
+ ElnaLexerClass.invalid, -- 0xF5
+ ElnaLexerClass.invalid, -- 0xF6
+ ElnaLexerClass.invalid, -- 0xF7
+ ElnaLexerClass.invalid, -- 0xF8
+ ElnaLexerClass.invalid, -- 0xF9
+ ElnaLexerClass.invalid, -- 0xFA
+ ElnaLexerClass.invalid, -- 0xFB
+ ElnaLexerClass.invalid, -- 0xFC
+ ElnaLexerClass.invalid, -- 0xFD
+ ElnaLexerClass.invalid, -- 0xFE
+ ElnaLexerClass.invalid -- 0xFF
+ ]
+ transition_table: [cast(#max(ElnaLexerState): Word)][cast(#max(ElnaLexerClass): Word)]ElnaLexerState
+
+(*
+ @param current_state First index into transitions table.
+ @param character_class Second index into transitions table.
+ @param next_state Next state to assign.
+*)
+proc elna_lexer_set_transition(current_state: ElnaLexerState; character_class: ElnaLexerClass;
+ next_state: ElnaLexerState)
+ transition_table[cast(current_state: Word)][cast(character_class: Word)] := next_state
+return
+
+(*
+ Sets same state transition for all character classes in one transition row.
+
+ @param current_state Current state.
+ @param next_state Next state.
+*)
+proc elna_lexer_default_transition(current_state, next_state: ElnaLexerState)
+var
+ current := cast(#min(ElnaLexerClass): Int)
+begin
+ while current <= cast(#max(ElnaLexerClass): Int) do
+ elna_lexer_set_transition(current_state, cast(current: ElnaLexerClass), next_state);
+ current := current + 1
+ end
+return
+
+(*
+ The transition table describes transitions from one state to another, given
+ a symbol (character class).
+
+ The table has m rows and n columns, where m is the amount of states and n is
+ the amount of classes. So given the current state and a classified character
+ the table can be used to look up the next state.
+*)
+proc elna_lexer_transitions*()
+var
+ current := cast(#min(ElnaLexerState): Int)
+begin
+ -- Every transition leads to the dead state by default;
+ -- valid transitions are filled in below.
+ while current <= cast(#max(ElnaLexerState): Int) do
+ elna_lexer_default_transition(cast(current: ElnaLexerState), ElnaLexerState.dead);
+ current := current + 1
+ end;
+ -- Start state.
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.zero, ElnaLexerState.leading_zero);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.digit, ElnaLexerState.integer);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.exponent, ElnaLexerState.identifier);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.alpha, ElnaLexerState.identifier);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.space, ElnaLexerState.skip);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.new_line, ElnaLexerState.skip);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.colon, ElnaLexerState.colon);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.equals, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.left_paren, ElnaLexerState.left_paren);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.right_paren, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.asterisk, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.backquote, ElnaLexerState.wide);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.brace, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.square, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.eof, ElnaLexerState.eof);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.dot, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.plus, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.minus, ElnaLexerState.minus);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.single_quote, ElnaLexerState.narrow);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.double_quote, ElnaLexerState.string);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.greater, ElnaLexerState.greater);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.less, ElnaLexerState.less);
+ elna_lexer_set_transition(ElnaLexerState.start, ElnaLexerClass.number_sign, ElnaLexerState.number_sign);
+
+ -- Identifier state.
+ elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.zero, ElnaLexerState.identifier);
+ elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.digit, ElnaLexerState.identifier);
+ elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.exponent, ElnaLexerState.identifier);
+ elna_lexer_set_transition(ElnaLexerState.identifier, ElnaLexerClass.alpha, ElnaLexerState.identifier);
+
+ -- Skip whitespaces.
+ elna_lexer_set_transition(ElnaLexerState.skip, ElnaLexerClass.space, ElnaLexerState.skip);
+ elna_lexer_set_transition(ElnaLexerState.skip, ElnaLexerClass.new_line, ElnaLexerState.skip);
+
+ -- Colon state.
+ elna_lexer_set_transition(ElnaLexerState.colon, ElnaLexerClass.equals, ElnaLexerState.stop);
+
+ -- Left paren state.
+ elna_lexer_set_transition(ElnaLexerState.left_paren, ElnaLexerClass.asterisk, ElnaLexerState.docblock);
+
+ -- Minus state.
+ elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.greater, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.minus, ElnaLexerClass.minus, ElnaLexerState.comment);
+
+ -- Greater state.
+ elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.greater, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.greater, ElnaLexerClass.equals, ElnaLexerState.stop);
+
+ -- Less state.
+ elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.less, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.equals, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.less, ElnaLexerClass.greater, ElnaLexerState.stop);
+
+ -- Numbers prefixed by 0 followed by a character (0b, 0x).
+ elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.zero, ElnaLexerState.integer);
+ elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.digit, ElnaLexerState.integer);
+ elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.alpha, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.exponent, ElnaLexerState.exponent);
+ elna_lexer_set_transition(ElnaLexerState.leading_zero, ElnaLexerClass.dot, ElnaLexerState.dot_after);
+
+ -- Decimal number.
+ elna_lexer_set_transition(ElnaLexerState.integer, ElnaLexerClass.zero, ElnaLexerState.integer);
+ elna_lexer_set_transition(ElnaLexerState.integer, ElnaLexerClass.digit, ElnaLexerState.integer);
+ elna_lexer_set_transition(ElnaLexerState.integer, ElnaLexerClass.alpha, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.integer, ElnaLexerClass.exponent, ElnaLexerState.exponent);
+ elna_lexer_set_transition(ElnaLexerState.integer, ElnaLexerClass.dot, ElnaLexerState.dot_after);
+
+ -- Comment.
+ elna_lexer_default_transition(ElnaLexerState.comment, ElnaLexerState.comment);
+ elna_lexer_set_transition(ElnaLexerState.comment, ElnaLexerClass.new_line, ElnaLexerState.dead);
+ elna_lexer_set_transition(ElnaLexerState.comment, ElnaLexerClass.eof, ElnaLexerState.dead);
+
+ -- Docblock.
+ elna_lexer_default_transition(ElnaLexerState.docblock, ElnaLexerState.docblock);
+ elna_lexer_set_transition(ElnaLexerState.docblock, ElnaLexerClass.asterisk, ElnaLexerState.closing_docblock);
+ elna_lexer_set_transition(ElnaLexerState.docblock, ElnaLexerClass.invalid, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.docblock, ElnaLexerClass.eof, ElnaLexerState.reject);
+
+ -- Closing docblock.
+ elna_lexer_default_transition(ElnaLexerState.closing_docblock, ElnaLexerState.docblock);
+ elna_lexer_set_transition(ElnaLexerState.closing_docblock, ElnaLexerClass.right_paren, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.closing_docblock, ElnaLexerClass.asterisk, ElnaLexerState.closing_docblock);
+ elna_lexer_set_transition(ElnaLexerState.closing_docblock, ElnaLexerClass.invalid, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.closing_docblock, ElnaLexerClass.eof, ElnaLexerState.reject);
+
+ -- Tail is the last part of the number containing digits and an optional type suffix.
+ elna_lexer_set_transition(ElnaLexerState.number_tail, ElnaLexerClass.exponent, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.number_tail, ElnaLexerClass.alpha, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.number_tail, ElnaLexerClass.zero, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.number_tail, ElnaLexerClass.digit, ElnaLexerState.number_tail);
+
+ -- Seen a period after a number, possible floating point number.
+ elna_lexer_set_transition(ElnaLexerState.dot_after, ElnaLexerClass.zero, ElnaLexerState.fraction);
+ elna_lexer_set_transition(ElnaLexerState.dot_after, ElnaLexerClass.digit, ElnaLexerState.fraction);
+
+ -- Entering floating point number fraction part after a decimal point.
+ elna_lexer_set_transition(ElnaLexerState.fraction, ElnaLexerClass.zero, ElnaLexerState.fraction);
+ elna_lexer_set_transition(ElnaLexerState.fraction, ElnaLexerClass.digit, ElnaLexerState.fraction);
+ elna_lexer_set_transition(ElnaLexerState.fraction, ElnaLexerClass.exponent, ElnaLexerState.exponent);
+ elna_lexer_set_transition(ElnaLexerState.fraction, ElnaLexerClass.alpha, ElnaLexerState.number_tail);
+
+ -- An exponent state begins with e or E.
+ elna_lexer_set_transition(ElnaLexerState.exponent, ElnaLexerClass.plus, ElnaLexerState.exponent_sign);
+ elna_lexer_set_transition(ElnaLexerState.exponent, ElnaLexerClass.minus, ElnaLexerState.exponent_sign);
+ elna_lexer_set_transition(ElnaLexerState.exponent, ElnaLexerClass.zero, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.exponent, ElnaLexerClass.digit, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.exponent, ElnaLexerClass.exponent, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.exponent, ElnaLexerClass.alpha, ElnaLexerState.number_tail);
+
+ -- Optional plus or minus sign following the exponent.
+ elna_lexer_set_transition(ElnaLexerState.exponent_sign, ElnaLexerClass.zero, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.exponent_sign, ElnaLexerClass.digit, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.exponent_sign, ElnaLexerClass.exponent, ElnaLexerState.number_tail);
+ elna_lexer_set_transition(ElnaLexerState.exponent_sign, ElnaLexerClass.alpha, ElnaLexerState.number_tail);
+
+ -- Attribute, generic parameter or trait marker ('#').
+ elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.exponent, ElnaLexerState.trait);
+ elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.alpha, ElnaLexerState.trait);
+ elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.square, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.number_sign, ElnaLexerClass.brace, ElnaLexerState.stop);
+
+ -- Trait identifier.
+ elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.exponent, ElnaLexerState.trait);
+ elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.alpha, ElnaLexerState.trait);
+ elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.zero, ElnaLexerState.trait);
+ elna_lexer_set_transition(ElnaLexerState.trait, ElnaLexerClass.digit, ElnaLexerState.trait);
+
+ -- Entering UTF-32 character literal.
+ elna_lexer_default_transition(ElnaLexerState.wide, ElnaLexerState.wide);
+ elna_lexer_set_transition(ElnaLexerState.wide, ElnaLexerClass.backslash, ElnaLexerState.wide_escape);
+ elna_lexer_set_transition(ElnaLexerState.wide, ElnaLexerClass.backquote, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.wide, ElnaLexerClass.eof, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.wide, ElnaLexerClass.invalid, ElnaLexerState.reject);
+
+ elna_lexer_default_transition(ElnaLexerState.wide_escape, ElnaLexerState.wide);
+ elna_lexer_set_transition(ElnaLexerState.wide_escape, ElnaLexerClass.eof, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.wide_escape, ElnaLexerClass.invalid, ElnaLexerState.reject);
+
+ -- Entering ASCII character literal.
+ elna_lexer_default_transition(ElnaLexerState.narrow, ElnaLexerState.narrow);
+ elna_lexer_set_transition(ElnaLexerState.narrow, ElnaLexerClass.backslash, ElnaLexerState.narrow_escape);
+ elna_lexer_set_transition(ElnaLexerState.narrow, ElnaLexerClass.backquote, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.narrow, ElnaLexerClass.eof, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.narrow, ElnaLexerClass.invalid, ElnaLexerState.reject);
+
+ elna_lexer_default_transition(ElnaLexerState.narrow_escape, ElnaLexerState.narrow);
+ elna_lexer_set_transition(ElnaLexerState.narrow_escape, ElnaLexerClass.eof, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.narrow_escape, ElnaLexerClass.invalid, ElnaLexerState.reject);
+
+ -- Entering UTF-8 string literal.
+ elna_lexer_default_transition(ElnaLexerState.string, ElnaLexerState.string);
+ elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.backslash, ElnaLexerState.string_escape);
+ elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.backquote, ElnaLexerState.stop);
+ elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.eof, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.string, ElnaLexerClass.invalid, ElnaLexerState.reject);
+
+ elna_lexer_default_transition(ElnaLexerState.string_escape, ElnaLexerState.string);
+ elna_lexer_set_transition(ElnaLexerState.string_escape, ElnaLexerClass.eof, ElnaLexerState.reject);
+ elna_lexer_set_transition(ElnaLexerState.string_escape, ElnaLexerClass.invalid, ElnaLexerState.reject)
+return
+
+end.
diff --git a/source/elna/source.elna b/source/elna/source.elna
new file mode 100644
index 0000000..7dfc2ac
--- /dev/null
+++ b/source/elna/source.elna
@@ -0,0 +1,20 @@
+(* 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/. *)
+
+(*
+ Source code representation.
+*)
+module
+
+type
+ ElnaSourceLocation* = record
+ line: Word;
+ column: Word
+ end
+ ElnaSourcePosition* = record
+ start_location: ElnaSourceLocation;
+ end_location: ElnaSourceLocation
+ end
+
+end.
diff --git a/source/main.elna b/source/main.elna
index b45b283..12486bd 100644
--- a/source/main.elna
+++ b/source/main.elna
@@ -2,5 +2,8 @@
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/. *)
program()
+import elna.lexer
+begin
+ elna_lexer_transitions()
end.
diff --git a/source/std.elna b/source/std.elna
new file mode 100644
index 0000000..33612dc
--- /dev/null
+++ b/source/std.elna
@@ -0,0 +1,6 @@
+module
+
+type
+ String* = []Word8
+
+end.
diff --git a/testsuite/fail_compilation/array_dimension_size.elna b/testsuite/fail_compilation/array_dimension_size.elna
new file mode 100644
index 0000000..bd5bb7c
--- /dev/null
+++ b/testsuite/fail_compilation/array_dimension_size.elna
@@ -0,0 +1,8 @@
+module
+
+proc f()
+var
+ a: [0]Int (* @Error Array dimension must be greater than zero *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/array_dimension_type.elna b/testsuite/fail_compilation/array_dimension_type.elna
new file mode 100644
index 0000000..e4720c2
--- /dev/null
+++ b/testsuite/fail_compilation/array_dimension_type.elna
@@ -0,0 +1,10 @@
+module
+
+type Color = (red, green, blue)
+
+proc f()
+var
+ a: [Color.red]Int (* @Error Array dimension must be of type 'Word' or 'Int', but got 'Color' *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/array_index_type.elna b/testsuite/fail_compilation/array_index_type.elna
new file mode 100644
index 0000000..e3081df
--- /dev/null
+++ b/testsuite/fail_compilation/array_index_type.elna
@@ -0,0 +1,11 @@
+module
+
+proc f(i: Word8)
+var
+ a: [10]Int
+ x: Int
+begin
+ x := a[i] (* @Error Array index must be of type 'Word' or 'Int', but got 'Word8' *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/constant_enum_to_int.elna b/testsuite/fail_compilation/constant_enum_to_int.elna
index 194109b..60bfeae 100644
--- a/testsuite/fail_compilation/constant_enum_to_int.elna
+++ b/testsuite/fail_compilation/constant_enum_to_int.elna
@@ -7,5 +7,5 @@ var
y: []Int
begin
- y := x[Enumeration.one to Enumeration.two] (* @Error Array index must be an integral type, but got 'Enumeration' *)
+ y := x[Enumeration.one to Enumeration.two] (* @Error Array index must be of type 'Word' or 'Int', but got 'Enumeration' *)
end.
diff --git a/testsuite/runnable/backquote_escape.elna b/testsuite/runnable/backquote_escape.elna
new file mode 100644
index 0000000..b61e71c
--- /dev/null
+++ b/testsuite/runnable/backquote_escape.elna
@@ -0,0 +1,4 @@
+program()
+begin
+ assert(`\`` = `\{U+60}`)
+end.