Merge GCC frontend into the branch

This commit is contained in:
2025-12-02 10:22:06 +01:00
parent 5f7d839741
commit 23b6f074c7
53 changed files with 13330 additions and 0 deletions

174
gcc/Make-lang.in Normal file
View File

@@ -0,0 +1,174 @@
# Top level -*- makefile -*- fragment for the Elna frontend.
# Copyright (C) 2025 Free Software Foundation, Inc.
# GCC 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, or (at your option)
# any later version.
# GCC 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 GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
ELNA_INSTALL_NAME := $(shell echo gelna|sed '$(program_transform_name)')
ELNA_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gelna|sed '$(program_transform_name)')
elna: elna1$(exeext)
.PHONY: elna
# Driver
ELNA_OBJS = \
$(GCC_OBJS) \
elna/elna-spec.o \
$(END)
gelna$(exeext): $(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
$(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
$(EXTRA_GCC_LIBS) $(LIBS)
# Create a version of the gelna driver which calls the cross-compiler.
gelna-cross$(exeext): gelna$(exeext)
-rm -f gelna-cross$(exeext)
cp gelna$(exeext) gelna-cross$(exeext)
# The compiler proper
elna_OBJS = \
elna/elna1.o \
elna/elna-generic.o \
elna/elna-diagnostic.o \
elna/elna-tree.o \
elna/elna-builtins.o \
elna/ast.o \
elna/dependency.o \
elna/driver.o \
elna/lexer.o \
elna/parser.o \
elna/semantic.o \
elna/symbol.o \
elna/result.o \
$(END)
elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
attribs.o $(elna_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS)
elna.all.cross: gelna-cross$(exeext)
elna.start.encap: gelna$(exeext)
elna.rest.encap:
# No elna-specific selftests.
selftest-elna:
ELNA_TEXI_FILES = \
elna/gcc/gelna.texi \
$(srcdir)/doc/include/fdl.texi \
$(srcdir)/doc/include/gpl_v3.texi \
$(srcdir)/doc/include/funding.texi \
$(srcdir)/doc/include/gcc-common.texi \
gcc-vers.texi
elna.install-common: installdirs
-rm -f $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
$(INSTALL_PROGRAM) gelna$(exeext) $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
-if test -f elna1$(exeext); then \
if test -f gelna-cross$(exeext); then \
:; \
else \
rm -f $(DESTDIR)$(bindir)/$(ELNA_TARGET_INSTALL_NAME)$(exeext); \
( cd $(DESTDIR)$(bindir) && \
$(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \
fi; \
fi
$(build_htmldir)/gelna/index.html: $(ELNA_TEXI_FILES)
$(mkinstalldirs) $(@D)
rm -f $(@D)/*
$(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/elna -o $(@D) $<
# Required goals, they still do nothing
elna.install-man:
elna.install-info:
elna.install-pdf:
elna.install-plugin:
elna.install-html: $(build_htmldir)/gelna
@$(NORMAL_INSTALL)
test -z "$(htmldir)" || $(mkinstalldirs) "$(DESTDIR)$(htmldir)"
@for p in $(build_htmldir)/gelna; do \
if test -f "$$p" || test -d "$$p"; then d=""; else d="$(srcdir)/"; fi; \
f=$(html__strip_dir) \
if test -d "$$d$$p"; then \
echo " $(mkinstalldirs) '$(DESTDIR)$(htmldir)/$$f'"; \
$(mkinstalldirs) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \
echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \
$(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f"; \
else \
echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(htmldir)/$$f'"; \
$(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(htmldir)/$$f"; \
fi; \
done
elna.info:
elna.dvi:
elna.pdf:
elna.html: $(build_htmldir)/gelna/index.html
elna.man:
elna.mostlyclean:
elna.clean:
elna.distclean:
elna.maintainer-clean:
# make uninstall
elna.uninstall:
-rm -f gelna$(exeext) elna1$(exeext)
-rm -f $(elna_OBJS)
# Used for handling bootstrap
elna.stage1: stage1-start
-mv elna/*$(objext) stage1/elna
elna.stage2: stage2-start
-mv elna/*$(objext) stage2/elna
elna.stage3: stage3-start
-mv elna/*$(objext) stage3/elna
elna.stage4: stage4-start
-mv elna/*$(objext) stage4/elna
elna.stageprofile: stageprofile-start
-mv elna/*$(objext) stageprofile/elna
elna.stagefeedback: stagefeedback-start
-mv elna/*$(objext) stagefeedback/elna
ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated
ELNA_CXXFLAGS = -std=c++17
elna/%.o: elna/frontend/%.cc elna/generated/parser.hh elna/generated/location.hh
$(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<
$(POSTCOMPILE)
elna/%.o: elna/generated/%.cc elna/generated/parser.hh elna/generated/location.hh
$(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<
$(POSTCOMPILE)
elna/%.o: elna/gcc/%.cc elna/generated/parser.hh elna/generated/location.hh
$(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<
$(POSTCOMPILE)
elna/generated/parser.cc: elna/frontend/parser.yy
mkdir -p $(dir $@)
$(BISON) -d -o $@ $<
elna/generated/parser.hh elna/generated/location.hh: elna/generated/parser.cc
@touch $@
elna/generated/lexer.cc: elna/frontend/lexer.ll
mkdir -p $(dir $@)
$(FLEX) -o $@ $<

42
gcc/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Elna programming language
## Current implementation
This repository contains a GCC frontend for Elna. After finishing the frontend
I'm planning to rewrite the compiler in Elna itself with its own backend and
a hand-written parser. So GCC gives a way to have a simple bootstrap compiler
and a possbility to compile Elna programs for different platforms.
## Grammar
Flex and bison grammar specifications, `lexer.ll` and `parser.yy`, can be found
in the `boot/` directory.
## Build
The frontend requires GCC 15.2.0 (not tested with other versions).
Download the GCC source. Copy the contents of this repository into `gcc/elna`
inside GCC. Finally build GCC enabling the frontend with
`--enable-languages=c,c++,elna`. After the installation the compiler can be
invoked with `$prefix/bin/gelna`.
There is also a `Rakefile` that downloads, builds and installs GCC into the
`./build/` subdirectory. The `Rakefile` assumes that ruby and rake, as well as
all GCC dependencies are already available in the system. It works under Linux
and Mac OS. In the latter case GCC is patched with the patches used by Homebrew
(official GCC doesn't support Apple silicon targets). Invoke with
```sh
rake boot
```
`gcc` binary is used by default, but a different gcc version can be specified
by passing `CC` and `CXX` environment variables to rake, e.g.:
```sh
rake CC=gcc-15 CXX=g++-15 boot
```
See `rake -T` for more tasks. The GCC source is under `build/tools`. The
installation path is `build/host/install`.

37
gcc/config-lang.in Normal file
View File

@@ -0,0 +1,37 @@
# Top level configure fragment for gcc Elna frontend.
# Copyright (C) 2025 Free Software Foundation, Inc.
# GCC 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, or (at your option)
# any later version.
# GCC 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 GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# Configure looks for the existence of this file to auto-config each language.
# We define several parameters used by configure:
#
# language - name of language as it would appear in $(LANGUAGES)
# boot_language - "yes" if we need to build this language in stage1
# compilers - value to add to $(COMPILERS)
language="elna"
gcc_subdir="elna/gcc"
compilers="elna1\$(exeext)"
target_libs=""
gtfiles="\$(srcdir)/elna/gcc/elna1.cc \$(srcdir)/elna/include/elna/gcc/elna1.h"
lang_requires_boot_languages=c++
# Do not build by default
build_by_default="no"

274
gcc/elna-builtins.cc Normal file
View File

@@ -0,0 +1,274 @@
/* Builtin definitions.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC 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, or (at your option)
any later version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include <algorithm>
#include "elna/gcc/elna-builtins.h"
#include "elna/gcc/elna1.h"
#include "stor-layout.h"
#include "stringpool.h"
#include "elna/gcc/elna-tree.h"
namespace elna::gcc
{
void init_ttree()
{
elna_int_type_node = long_integer_type_node;
elna_word_type_node = size_type_node;
elna_char_type_node = unsigned_char_type_node;
elna_pointer_type_node = ptr_type_node;
elna_float_type_node = double_type_node;
elna_bool_type_node = boolean_type_node;
elna_bool_true_node = boolean_true_node;
elna_bool_false_node = boolean_false_node;
elna_pointer_nil_node = null_pointer_node;
elna_string_type_node = make_node(RECORD_TYPE);
tree string_ptr_type = build_pointer_type_for_mode(elna_char_type_node, VOIDmode, true);
elna_string_length_field_node = build_field(UNKNOWN_LOCATION,
elna_string_type_node, "length", build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST));
elna_string_ptr_field_node = build_field(UNKNOWN_LOCATION,
elna_string_type_node, "ptr", build_qualified_type(string_ptr_type, TYPE_QUAL_CONST));
TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node);
layout_type(elna_string_type_node);
}
static
tree declare_builtin_type(std::shared_ptr<symbol_table> symbol_table, const char *name, tree type)
{
tree identifier = get_identifier(name);
tree type_declaration = build_decl(UNKNOWN_LOCATION, TYPE_DECL, identifier, type);
symbol_table->enter(name, type_declaration);
return type_declaration;
}
std::shared_ptr<symbol_table> builtin_symbol_table()
{
std::shared_ptr<elna::gcc::symbol_table> symbol_table = std::make_shared<elna::gcc::symbol_table>();
declare_builtin_type(symbol_table, "Int", elna_int_type_node);
declare_builtin_type(symbol_table, "Word", elna_word_type_node);
declare_builtin_type(symbol_table, "Char", elna_char_type_node);
declare_builtin_type(symbol_table, "Bool", elna_bool_type_node);
declare_builtin_type(symbol_table, "Pointer", elna_pointer_type_node);
declare_builtin_type(symbol_table, "Float", elna_float_type_node);
tree string_declaration = declare_builtin_type(symbol_table, "String", elna_string_type_node);
TYPE_NAME(elna_string_type_node) = DECL_NAME(string_declaration);
TYPE_STUB_DECL(elna_string_type_node) = string_declaration;
return symbol_table;
}
tree build_composite_type(const std::vector<frontend::type_field>& fields, tree composite_type_node,
std::shared_ptr<symbol_table> symbols)
{
for (auto& field : fields)
{
tree rewritten_field = get_inner_alias(field.second, symbols);
tree field_declaration = build_field(UNKNOWN_LOCATION,
composite_type_node, field.first, rewritten_field);
TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration);
}
layout_type(composite_type_node);
return composite_type_node;
}
tree build_procedure_type(const frontend::procedure_type& procedure, std::shared_ptr<symbol_table> symbols)
{
std::vector<tree> parameter_types(procedure.parameters.size());
for (std::size_t i = 0; i < procedure.parameters.size(); ++i)
{
parameter_types[i] = get_inner_alias(procedure.parameters.at(i), symbols);
}
tree return_type = void_type_node;
if (!procedure.return_type.proper_type.empty())
{
return_type = get_inner_alias(procedure.return_type.proper_type, symbols);
}
return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data());
}
tree get_inner_alias(const frontend::type& type, std::shared_ptr<symbol_table> symbols)
{
if (auto reference = type.get<frontend::primitive_type>())
{
auto looked_up = symbols->lookup(reference->identifier);
gcc_assert(looked_up != NULL_TREE);
return TREE_TYPE(looked_up);
}
else if (auto reference = type.get<frontend::record_type>())
{
tree composite_type_node = make_node(RECORD_TYPE);
build_composite_type(reference->fields, composite_type_node, symbols);
return composite_type_node;
}
else if (auto reference = type.get<frontend::union_type>())
{
tree composite_type_node = make_node(UNION_TYPE);
build_composite_type(reference->fields, composite_type_node, symbols);
return composite_type_node;
}
else if (auto reference = type.get<frontend::enumeration_type>())
{
return build_enumeration_type(reference->members);
}
else if (auto reference = type.get<frontend::pointer_type>())
{
return build_global_pointer_type(get_inner_alias(reference->base, symbols));
}
else if (auto reference = type.get<frontend::array_type>())
{
tree base = get_inner_alias(reference->base, symbols);
return build_static_array_type(base, reference->size);
}
else if (auto reference = type.get<frontend::procedure_type>())
{
auto procedure = build_procedure_type(*reference, symbols);
return build_global_pointer_type(procedure);
}
else if (auto reference = type.get<frontend::alias_type>())
{
return TREE_TYPE(handle_symbol(reference->name, reference, symbols));
}
return error_mark_node;
}
tree handle_symbol(const std::string& symbol_name, std::shared_ptr<frontend::alias_type> reference,
std::shared_ptr<symbol_table> symbols)
{
tree looked_up = symbols->lookup(symbol_name);
if (looked_up == NULL_TREE)
{
tree type_tree = get_inner_alias(reference->reference, symbols);
looked_up = build_decl(UNKNOWN_LOCATION, TYPE_DECL,
get_identifier(symbol_name.c_str()), type_tree);
TREE_PUBLIC(looked_up) = 1;
if (is_unique_type(type_tree))
{
TYPE_NAME(type_tree) = DECL_NAME(looked_up);
TYPE_STUB_DECL(type_tree) = looked_up;
}
else
{
TYPE_NAME(type_tree) = looked_up;
}
symbols->enter(symbol_name, looked_up);
}
return looked_up;
}
void declare_procedure(const std::string& name, const frontend::procedure_info& info,
std::shared_ptr<symbol_table> symbols)
{
tree declaration_type = gcc::build_procedure_type(info.symbol, symbols);
tree fndecl = build_fn_decl(name.c_str(), declaration_type);
symbols->enter(name, fndecl);
if (info.symbol.return_type.no_return)
{
TREE_THIS_VOLATILE(fndecl) = 1;
}
tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, TREE_TYPE(declaration_type));
DECL_CONTEXT(resdecl) = fndecl;
DECL_RESULT(fndecl) = resdecl;
tree argument_chain = NULL_TREE;
function_args_iterator parameter_type;
function_args_iter_init(&parameter_type, declaration_type);
std::vector<std::string>::const_iterator parameter_name = info.names.cbegin();
for (frontend::type parameter : info.symbol.parameters)
{
tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL,
get_identifier(parameter_name->c_str()), function_args_iter_cond(&parameter_type));
DECL_CONTEXT(declaration_tree) = fndecl;
DECL_ARG_TYPE(declaration_tree) = function_args_iter_cond(&parameter_type);
argument_chain = chainon(argument_chain, declaration_tree);
function_args_iter_next(&parameter_type);
++parameter_name;
}
DECL_ARGUMENTS(fndecl) = argument_chain;
TREE_ADDRESSABLE(fndecl) = 1;
DECL_EXTERNAL(fndecl) = info.is_extern();
TREE_PUBLIC(fndecl) = info.exported;
}
tree declare_variable(const std::string& name, const frontend::variable_info& info,
std::shared_ptr<symbol_table> symbols)
{
auto variable_type = get_inner_alias(info.symbol, symbols);
tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type);
TREE_ADDRESSABLE(declaration_tree) = 1;
DECL_EXTERNAL(declaration_tree) = info.is_extern;
TREE_PUBLIC(declaration_tree) = info.exported;
symbols->enter(name, declaration_tree);
return declaration_tree;
}
void declare_type(const std::string& name, const frontend::type_info& info, std::shared_ptr<symbol_table> symbols)
{
// The top level symbol table has basic (builtin) types in it which are not aliases.
if (auto alias_type = info.symbol.get<frontend::alias_type>())
{
tree type_declaration = handle_symbol(name, alias_type, symbols);
TREE_PUBLIC(type_declaration) = info.exported;
}
}
void rewrite_symbol_table(std::shared_ptr<frontend::symbol_table> info_table, std::shared_ptr<symbol_table> symbols)
{
for (auto& [symbol_name, symbol_info] : *info_table)
{
if (auto type_info = symbol_info->is_type())
{
declare_type(symbol_name, *type_info, symbols);
}
else if (auto variable_info = symbol_info->is_variable())
{
declare_variable(symbol_name, *variable_info, symbols);
}
else if (auto procedure_info = symbol_info->is_procedure())
{
declare_procedure(symbol_name, *procedure_info, symbols);
}
}
}
}

167
gcc/elna-diagnostic.cc Normal file
View File

@@ -0,0 +1,167 @@
/* Elna frontend specific diagnostic routines.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC 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, or (at your option)
any later version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna1.h"
namespace elna::gcc
{
linemap_guard::linemap_guard(const char *filename)
{
linemap_add(line_table, LC_ENTER, 0, filename, 1);
}
linemap_guard::~linemap_guard()
{
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
}
location_t get_location(const frontend::position *position)
{
linemap_line_start(line_table, position->line, 0);
return linemap_position_for_column(line_table, position->column);
}
std::string print_aggregate_name(tree type, const std::string& kind_name)
{
if (TYPE_IDENTIFIER(type) == NULL_TREE)
{
return kind_name;
}
else
{
return std::string(IDENTIFIER_POINTER(TYPE_IDENTIFIER(type)));
}
}
std::string print_type(tree type)
{
gcc_assert(TYPE_P(type));
tree unqualified_type = get_qualified_type(type, TYPE_UNQUALIFIED);
tree_code code = TREE_CODE(type);
if (unqualified_type == elna_int_type_node)
{
return "Int";
}
else if (unqualified_type == elna_word_type_node)
{
return "Word";
}
else if (unqualified_type == elna_bool_type_node)
{
return "Bool";
}
else if (unqualified_type == elna_pointer_type_node)
{
return "Pointer";
}
else if (unqualified_type == elna_float_type_node)
{
return "Float";
}
else if (unqualified_type == elna_char_type_node)
{
return "Char";
}
else if (unqualified_type == elna_string_type_node)
{
return "String";
}
else if (is_void_type(unqualified_type)) // For procedures without a return type.
{
return "()";
}
else if (POINTER_TYPE_P(unqualified_type))
{
tree pointer_target_type = TREE_TYPE(type);
if (TREE_CODE(pointer_target_type) == FUNCTION_TYPE)
{
return print_type(pointer_target_type);
}
else
{
return std::string("^" + print_type(pointer_target_type));
}
}
else if (code == FUNCTION_TYPE)
{
std::string output = "proc(";
tree parameter_type = TYPE_ARG_TYPES(type);
while (TREE_VALUE(parameter_type) != void_type_node)
{
output += print_type(TREE_VALUE(parameter_type));
parameter_type = TREE_CHAIN(parameter_type);
if (TREE_VALUE(parameter_type) == void_type_node)
{
break;
}
else
{
output += ", ";
}
}
output += ')';
tree return_type = TREE_TYPE(type);
if (!is_void_type(return_type))
{
output += " -> " + print_type(return_type);
}
return output;
}
else if (code == ARRAY_TYPE)
{
return "array";
}
else if (code == RECORD_TYPE)
{
return print_aggregate_name(unqualified_type, "record");
}
else if (code == UNION_TYPE)
{
return print_aggregate_name(unqualified_type, "union");
}
else if (code == ENUMERAL_TYPE)
{
return print_aggregate_name(unqualified_type, "enumeration");
}
else
{
return "<<unknown-type>>";
}
gcc_unreachable();
}
void report_errors(const std::deque<std::unique_ptr<frontend::error>>& errors)
{
for (const auto& error : errors)
{
location_t gcc_location{ UNKNOWN_LOCATION };
if (error->position.line != 0 || error->position.column != 0)
{
gcc_location = elna::gcc::get_location(&error->position);
}
error_at(gcc_location, error->what().c_str());
}
}
}

1277
gcc/elna-generic.cc Normal file

File diff suppressed because it is too large Load Diff

31
gcc/elna-spec.cc Normal file
View File

@@ -0,0 +1,31 @@
/* Specific flags and argument handling of the Elna front end.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC 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, or (at your option)
any later version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
void lang_specific_driver(struct cl_decoded_option ** /* in_decoded_options */,
unsigned int * /* in_decoded_options_count */,
int * /*in_added_libraries */)
{
}
/* Called before linking. Returns 0 on success and -1 on failure. */
int lang_specific_pre_link(void)
{
return 0;
}
/* Number of extra output files that lang_specific_pre_link may generate. */
int lang_specific_extra_outfiles = 0;

315
gcc/elna-tree.cc Normal file
View File

@@ -0,0 +1,315 @@
/* Utilities to manipulate GCC trees.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC 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, or (at your option)
any later version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna1.h"
#include "function.h"
#include "stor-layout.h"
#include "diagnostic-core.h"
namespace elna::gcc
{
bool is_integral_type(tree type)
{
gcc_assert(TYPE_P(type));
return TREE_CODE(type) == INTEGER_TYPE && type != elna_char_type_node;
}
bool is_numeric_type(tree type)
{
return is_integral_type(type) || type == elna_float_type_node;
}
bool is_unique_type(tree type)
{
gcc_assert(TYPE_P(type));
return RECORD_OR_UNION_TYPE_P(type) || TREE_CODE(type) == ENUMERAL_TYPE;
}
bool is_void_type(tree type)
{
return type == NULL_TREE || type == void_type_node;
}
bool is_castable_type(tree type)
{
gcc_assert(TYPE_P(type));
return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE;
}
bool are_compatible_pointers(tree lhs_type, tree rhs)
{
gcc_assert(TYPE_P(lhs_type));
tree rhs_type = TREE_TYPE(rhs);
return (POINTER_TYPE_P(lhs_type) && rhs == elna_pointer_nil_node)
|| (POINTER_TYPE_P(lhs_type) && lhs_type == rhs_type);
}
tree prepare_rvalue(tree rvalue)
{
if (DECL_P(rvalue) && TREE_CODE(TREE_TYPE(rvalue)) == FUNCTION_TYPE)
{
return build1(ADDR_EXPR, build_pointer_type_for_mode(TREE_TYPE(rvalue), VOIDmode, true), rvalue);
}
else
{
return rvalue;
}
}
bool is_assignable_from(tree assignee, tree assignment)
{
return get_qualified_type(TREE_TYPE(assignment), TYPE_UNQUALIFIED) == assignee
|| are_compatible_pointers(assignee, assignment);
}
void append_statement(tree statement_tree)
{
if (!vec_safe_is_empty(f_binding_level->defers))
{
append_to_statement_list(statement_tree, &f_binding_level->defers->begin()->try_statements);
}
else
{
append_to_statement_list(statement_tree, &f_binding_level->statement_list);
}
}
void defer(tree statement_tree)
{
defer_scope new_defer{ statement_tree, alloc_stmt_list() };
vec_safe_insert(f_binding_level->defers, 0, new_defer);
}
tree chain_defer()
{
if (vec_safe_is_empty(f_binding_level->defers))
{
return f_binding_level->statement_list;
}
defer_scope *defer_iterator = f_binding_level->defers->begin();
tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node,
defer_iterator->try_statements, defer_iterator->defer_block);
int i;
FOR_EACH_VEC_ELT_FROM(*f_binding_level->defers, i, defer_iterator, 1)
{
append_to_statement_list(defer_tree, &defer_iterator->try_statements);
defer_tree = build2(TRY_FINALLY_EXPR, void_type_node,
defer_iterator->try_statements, defer_iterator->defer_block);
}
return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), f_binding_level->statement_list, defer_tree);
}
tree build_field(location_t location, tree record_type, const std::string name, tree type)
{
tree field_declaration = build_decl(location,
FIELD_DECL, get_identifier(name.c_str()), type);
TREE_ADDRESSABLE(field_declaration) = 1;
DECL_CONTEXT(field_declaration) = record_type;
return field_declaration;
}
tree do_pointer_arithmetic(frontend::binary_operator binary_operator,
tree left, tree right, location_t operation_location)
{
tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED);
tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED);
if (binary_operator == frontend::binary_operator::sum)
{
tree pointer{ NULL_TREE };
tree offset{ NULL_TREE };
tree pointer_type{ NULL_TREE };
if (POINTER_TYPE_P(left_type) && is_integral_type(right_type))
{
pointer = left;
offset = right;
pointer_type = left_type;
}
else if (is_integral_type(left_type) && POINTER_TYPE_P(right_type))
{
pointer = right;
offset = left;
pointer_type = right_type;
}
else
{
return error_mark_node;
}
tree size_exp = pointer_type == elna_pointer_type_node
? size_one_node
: fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer))));
offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp);
offset = fold_convert(sizetype, offset);
return fold_build2_loc(operation_location, POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset);
}
else if (binary_operator == frontend::binary_operator::subtraction)
{
if (POINTER_TYPE_P(left_type) && is_integral_type(right_type))
{
tree pointer_type = left_type;
tree offset_type = right_type;
tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type)));
tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp);
convert_expression = fold_convert(sizetype, convert_expression);
convert_expression = fold_build1(NEGATE_EXPR, sizetype, convert_expression);
return fold_build2_loc(operation_location, POINTER_PLUS_EXPR, pointer_type, left, convert_expression);
}
else if (POINTER_TYPE_P(left_type) && POINTER_TYPE_P(right_type) && left_type == right_type)
{
return fold_build2_loc(operation_location, POINTER_DIFF_EXPR, ssizetype, left, right);
}
}
gcc_unreachable();
}
tree build_binary_operation(bool condition, frontend::binary_expression *expression,
tree_code operator_code, tree left, tree right, tree target_type)
{
location_t expression_location = get_location(&expression->position());
tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED);
tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED);
if (condition)
{
return fold_build2_loc(expression_location, operator_code, target_type, left, right);
}
else
{
error_at(expression_location,
"invalid operands of type '%s' and '%s' for operator %s",
print_type(left_type).c_str(), print_type(right_type).c_str(),
elna::frontend::print_binary_operator(expression->operation()));
return error_mark_node;
}
}
tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name)
{
if (type == error_mark_node)
{
return type;
}
tree field_declaration = TYPE_FIELDS(type);
if (!RECORD_OR_UNION_TYPE_P(type))
{
error_at(expression_location, "Type '%s' does not have a field named '%s'",
print_type(type).c_str(), field_name.c_str());
return error_mark_node;
}
while (field_declaration != NULL_TREE)
{
tree declaration_name = DECL_NAME(field_declaration);
const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name);
if (field_name == identifier_pointer)
{
break;
}
field_declaration = TREE_CHAIN(field_declaration);
}
if (field_declaration == NULL_TREE)
{
error_at(expression_location, "Aggregate type does not have a field '%s'", field_name.c_str());
return error_mark_node;
}
return field_declaration;
}
tree build_global_pointer_type(tree type)
{
return build_pointer_type_for_mode(type, VOIDmode, true);
}
tree build_static_array_type(tree type, const std::uint64_t size)
{
tree upper_bound = build_int_cst_type(integer_type_node, size);
tree range_type = build_range_type(integer_type_node, size_one_node, upper_bound);
return build_array_type(type, range_type);
}
tree build_enumeration_type(const std::vector<std::string>& members)
{
tree composite_type_node = make_node(ENUMERAL_TYPE);
const tree base_type = integer_type_node;
TREE_TYPE(composite_type_node) = base_type;
ENUM_IS_SCOPED(composite_type_node) = 1;
tree *pp = &TYPE_VALUES(composite_type_node);
std::size_t order{ 1 };
for (const std::string& member : members)
{
tree member_name = get_identifier(member.c_str());
tree member_declaration = build_decl(UNKNOWN_LOCATION, CONST_DECL, member_name, composite_type_node);
DECL_CONTEXT(member_declaration) = composite_type_node;
DECL_INITIAL(member_declaration) = build_int_cst_type(composite_type_node, order++);
TREE_CONSTANT(member_declaration) = 1;
TREE_READONLY(member_declaration) = 1;
TYPE_MAX_VALUE(composite_type_node) = DECL_INITIAL(member_declaration);
*pp = build_tree_list(member_name, member_declaration);
pp = &TREE_CHAIN(*pp);
}
TYPE_MIN_VALUE(composite_type_node) = DECL_INITIAL(TREE_VALUE(TYPE_VALUES(composite_type_node)));
TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type);
SET_TYPE_ALIGN(composite_type_node, TYPE_ALIGN(base_type));
TYPE_SIZE(composite_type_node) = NULL_TREE;
TYPE_PRECISION(composite_type_node) = TYPE_PRECISION(base_type);
layout_type(composite_type_node);
return composite_type_node;
}
tree build_label_decl(const char *name, location_t loc)
{
auto label_decl = build_decl(loc, LABEL_DECL, get_identifier(name), void_type_node);
DECL_CONTEXT(label_decl) = current_function_decl;
return label_decl;
}
tree extract_constant(tree expression)
{
int code = TREE_CODE(expression);
if (code == CONST_DECL)
{
return DECL_INITIAL(expression);
}
else if (TREE_CODE_CLASS(code) == tcc_constant)
{
return expression;
}
return NULL_TREE;
}
}

256
gcc/elna1.cc Normal file
View File

@@ -0,0 +1,256 @@
/* Language-dependent hooks for Elna.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC 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, or (at your option)
any later version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "function.h"
#include "tree.h"
#include "elna/gcc/elna1.h"
#include "diagnostic.h"
#include "opts.h"
#include "debug.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include <fstream>
#include "elna/frontend/dependency.h"
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna-generic.h"
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna-builtins.h"
tree elna_global_trees[ELNA_TI_MAX];
hash_map<nofree_string_hash, tree> *elna_global_decls = nullptr;
/* The resulting tree type. */
union GTY ((desc("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
chain_next("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), "
"TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN "
"(&%h.generic)) : NULL"))) lang_tree_node
{
union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic;
};
/* Language hooks. */
static bool elna_langhook_init(void)
{
build_common_tree_nodes(false);
elna::gcc::init_ttree();
elna_global_decls = hash_map<nofree_string_hash, tree>::create_ggc(default_hash_map_size);
build_common_builtin_nodes();
return true;
}
using dependency_state = elna::frontend::dependency_state<std::shared_ptr<elna::gcc::symbol_table>>;
static elna::frontend::dependency elna_parse_file(dependency_state& state, const char *filename)
{
std::ifstream entry_point{ filename, std::ios::in };
if (!entry_point)
{
fatal_error(UNKNOWN_LOCATION, "Cannot open filename %s: %m", filename);
}
elna::gcc::linemap_guard{ filename };
elna::frontend::dependency outcome = elna::frontend::read_source(entry_point, filename);
if (outcome.has_errors())
{
elna::gcc::report_errors(outcome.errors());
return outcome;
}
elna::frontend::symbol_bag outcome_bag = elna::frontend::symbol_bag{ std::move(outcome.unresolved), state.globals };
for (const auto& sub_tree : outcome.tree->imports)
{
std::filesystem::path sub_path = "source" / elna::frontend::build_path(sub_tree->segments);
std::unordered_map<std::filesystem::path, elna::frontend::symbol_bag>::const_iterator cached_import =
state.cache.find(sub_path);
if (cached_import == state.cache.end())
{
elna_parse_file(state, sub_path.c_str());
cached_import = state.cache.find(sub_path);
}
outcome_bag.add_import(cached_import->second);
}
outcome.errors() = analyze_semantics(filename, outcome.tree, outcome_bag);
if (outcome.has_errors())
{
elna::gcc::report_errors(outcome.errors());
return outcome;
}
state.cache.insert({ filename, outcome_bag });
elna::gcc::rewrite_symbol_table(outcome_bag.leave(), state.custom);
return outcome;
}
static void elna_langhook_parse_file(void)
{
dependency_state state{ elna::gcc::builtin_symbol_table() };
for (unsigned int i = 0; i < num_in_fnames; i++)
{
elna::frontend::dependency outcome = elna_parse_file(state, in_fnames[i]);
linemap_add(line_table, LC_ENTER, 0, in_fnames[i], 1);
elna::gcc::generic_visitor generic_visitor{ state.custom, state.cache.find(in_fnames[i])->second };
outcome.tree->accept(&generic_visitor);
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
}
}
static tree elna_langhook_type_for_mode(enum machine_mode mode, int unsignedp)
{
if (mode == TYPE_MODE(float_type_node))
{
return float_type_node;
}
else if (mode == TYPE_MODE(double_type_node))
{
return double_type_node;
}
if (mode == TYPE_MODE(intQI_type_node))
{
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
}
else if (mode == TYPE_MODE(intHI_type_node))
{
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
}
else if (mode == TYPE_MODE(intSI_type_node))
{
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
}
else if (mode == TYPE_MODE(intDI_type_node))
{
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
}
else if (mode == TYPE_MODE(intTI_type_node))
{
return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
}
else if (mode == TYPE_MODE(integer_type_node))
{
return unsignedp ? unsigned_type_node : integer_type_node;
}
else if (mode == TYPE_MODE(long_integer_type_node))
{
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
}
else if (mode == TYPE_MODE(long_long_integer_type_node))
{
return unsignedp
? long_long_unsigned_type_node
: long_long_integer_type_node;
}
if (COMPLEX_MODE_P(mode))
{
if (mode == TYPE_MODE(complex_float_type_node))
{
return complex_float_type_node;
}
if (mode == TYPE_MODE(complex_double_type_node))
{
return complex_double_type_node;
}
if (mode == TYPE_MODE(complex_long_double_type_node))
{
return complex_long_double_type_node;
}
if (mode == TYPE_MODE(complex_integer_type_node) && !unsignedp)
{
return complex_integer_type_node;
}
}
/* gcc_unreachable */
return nullptr;
}
static bool global_bindings_p(void)
{
return current_function_decl == NULL_TREE;
}
static tree pushdecl(tree decl)
{
return decl;
}
static tree elna_langhook_builtin_function(tree decl)
{
elna_global_decls->put(IDENTIFIER_POINTER(DECL_NAME(decl)), decl);
return decl;
}
static unsigned int elna_langhook_option_lang_mask(void)
{
return CL_Elna;
}
/* Creates an expression whose value is that of EXPR, converted to type TYPE.
This function implements all reasonable scalar conversions. */
tree convert(tree type, tree expr)
{
if (error_operand_p(type) || error_operand_p(expr))
{
return error_mark_node;
}
if (TREE_TYPE(expr) == type)
{
return expr;
}
return error_mark_node;
}
#undef LANG_HOOKS_NAME
#define LANG_HOOKS_NAME "GNU Elna"
#undef LANG_HOOKS_INIT
#define LANG_HOOKS_INIT elna_langhook_init
#undef LANG_HOOKS_PARSE_FILE
#define LANG_HOOKS_PARSE_FILE elna_langhook_parse_file
#undef LANG_HOOKS_TYPE_FOR_MODE
#define LANG_HOOKS_TYPE_FOR_MODE elna_langhook_type_for_mode
#undef LANG_HOOKS_GETDECLS
#define LANG_HOOKS_GETDECLS hook_tree_void_null
#undef LANG_HOOKS_BUILTIN_FUNCTION
#define LANG_HOOKS_BUILTIN_FUNCTION elna_langhook_builtin_function
#undef LANG_HOOKS_IDENTIFIER_SIZE
#define LANG_HOOKS_IDENTIFIER_SIZE sizeof(struct tree_identifier)
#undef LANG_HOOKS_OPTION_LANG_MASK
#define LANG_HOOKS_OPTION_LANG_MASK elna_langhook_option_lang_mask
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
#include "gt-elna-elna1.h"
#include "gtype-elna.h"

135
gcc/gelna.texi Normal file
View File

@@ -0,0 +1,135 @@
\input texinfo @c -*-texinfo-*-
@setfilename gelna.info
@settitle The GNU Elna Compiler
@c Create a separate index for command line options
@defcodeindex op
@c Merge the standard indexes into a single one.
@syncodeindex fn cp
@syncodeindex vr cp
@syncodeindex ky cp
@syncodeindex pg cp
@syncodeindex tp cp
@include gcc-common.texi
@c Copyright years for this manual.
@set copyrights-elna 2025
@copying
@c man begin COPYRIGHT
Copyright @copyright{} @value{copyrights-elna} Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the
@c man end
section entitled ``GNU Free Documentation License''.
@ignore
@c man begin COPYRIGHT
man page gfdl(7).
@c man end
@end ignore
@end copying
@ifinfo
@format
@dircategory Software development
@direntry
* Gelna: (gelna). A GCC-based compiler for the Elna language
@end direntry
@end format
@insertcopying
@end ifinfo
@titlepage
@title The GNU Elna Compiler
@versionsubtitle
@author Eugen Wissner
@page
@vskip 0pt plus 1filll
Published by the Free Software Foundation @*
51 Franklin Street, Fifth Floor@*
Boston, MA 02110-1301, USA@*
@sp 1
@insertcopying
@end titlepage
@contents
@page
@node Top
@top Introduction
This manual describes how to use @command{gelna}, the GNU compiler for
the Elna programming language. This manual is specifically about how to
invoke @command{gelna}.
@menu
* Copying:: The GNU General Public License.
* GNU Free Documentation License::
How you can share and copy this manual.
* Invoking gelna:: How to run gelna.
* Option Index:: Index of command line options.
* Keyword Index:: Index of concepts.
@end menu
@include gpl_v3.texi
@include fdl.texi
@node Invoking gelna
@chapter Invoking gelna
@c man title gelna A GCC-based compiler for the Elna language
@ignore
@c man begin SYNOPSIS gelna
gelna [@option{-c}|@option{-S}]
[@option{-g}] [@option{-pg}]
[@option{-o} @var{outfile}] @var{infile}@dots{}
Only the most useful options are listed here; see below for the
remainder.
@c man end
@c man begin SEEALSO
gpl(7), gfdl(7), fsf-funding(7), gcc(1)
and the Info entries for @file{gelna} and @file{gcc}.
@c man end
@end ignore
@c man begin DESCRIPTION gelna
The @command{gelna} command is a frontend to @command{gcc} and
supports many of the same options. @xref{Option Summary, , Option
Summary, gcc, Using the GNU Compiler Collection (GCC)}. This manual
only documents the options specific to @command{gelna}.
@c man end
@c man begin OPTIONS gelna
@c man end
@node Option Index
@unnumbered Option Index
@command{gelna}'s command line options are indexed here without any
initial @samp{-} or @samp{--}. Where an option has both positive and
negative forms (such as -foption and -fno-option), relevant entries in
the manual are indexed under the most appropriate form; it may sometimes
be useful to look up both forms.
@printindex op
@node Keyword Index
@unnumbered Keyword Index
@printindex cp
@bye

28
gcc/lang-specs.h Normal file
View File

@@ -0,0 +1,28 @@
/* GCC driver specs for Elna frontend.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC 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, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* gcc/gcc.cc */
{".elna", "@elna", nullptr, 0, 0},
{"@elna",
"elna1 %i \
%{!Q:-quiet} " DUMPS_OPTIONS("") " %{m*} %{aux-info*} \
%{g*} %{O*} %{W*&pedantic*} %{w} %{std*&ansi&trigraphs} \
%{pg:-p} %{p} %{f*} %{undef} \
%{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}} \
%{fsyntax-only:-o %j} %{-param*} \
%{!fsyntax-only:%(invoke_as)}",
nullptr, 0, 0},

23
gcc/lang.opt Normal file
View File

@@ -0,0 +1,23 @@
; lang.opt -- Options for the Elna front end.
; Copyright (C) 2025 Free Software Foundation, Inc.
;
; GCC 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, or (at your option) any later
; version.
;
; GCC 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 GCC; see the file COPYING3. If not see
; <http://www.gnu.org/licenses/>.
; See the GCC internals manual for a description of this file's format.
; Please try to keep this file in ASCII collating order.
Language
Elna

2
gcc/lang.opt.urls Normal file
View File

@@ -0,0 +1,2 @@
; Autogenerated by regenerate-opt-urls.py from gcc/lang.opt and generated HTML