Use colon instead of as to cast

This commit is contained in:
2025-02-14 08:05:03 +01:00
parent c564847c6b
commit 8b987ee2b2
8 changed files with 186 additions and 86 deletions

View File

@ -21,3 +21,25 @@ and a possbility to compile Elna programs for different platforms.
Flex and bison grammar specifications, `lexer.ll` and `parser.yy`, can be found Flex and bison grammar specifications, `lexer.ll` and `parser.yy`, can be found
in the `boot/` directory. in the `boot/` directory.
## Build
The frontend requires GCC 14.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
```
See `rake -T` for more tasks. The GCC source is under `build/tools`. The
installation path is `build/host/install`.

View File

@ -134,9 +134,6 @@ return {
cast { cast {
return yy::parser::make_CAST(this->location); return yy::parser::make_CAST(this->location);
} }
as {
return yy::parser::make_AS(this->location);
}
sizeof { sizeof {
return yy::parser::make_SIZEOF(this->location); return yy::parser::make_SIZEOF(this->location);
} }

View File

@ -85,7 +85,7 @@ along with GCC; see the file COPYING3. If not see
%token CONST VAR PROCEDURE ARRAY OF TYPE RECORD POINTER TO UNION %token CONST VAR PROCEDURE ARRAY OF TYPE RECORD POINTER TO UNION
%token BEGIN_BLOCK END_BLOCK EXTERN DEFER %token BEGIN_BLOCK END_BLOCK EXTERN DEFER
%token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA %token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA
%token AND OR NOT CAST AS SIZEOF %token AND OR NOT CAST SIZEOF
%token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS %token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS
%token PLUS MINUS MULTIPLICATION DIVISION REMAINDER %token PLUS MINUS MULTIPLICATION DIVISION REMAINDER
%token ASSIGNMENT COLON HAT AT NIL ARROW %token ASSIGNMENT COLON HAT AT NIL ARROW
@ -189,7 +189,7 @@ call_expression: IDENTIFIER actual_parameter_list
$$ = new elna::boot::call_expression(elna::boot::make_position(@1), $1); $$ = new elna::boot::call_expression(elna::boot::make_position(@1), $1);
std::swap($$->arguments(), $2); std::swap($$->arguments(), $2);
} }
cast_expression: CAST LEFT_PAREN expression AS type_expression RIGHT_PAREN cast_expression: CAST LEFT_PAREN expression COLON type_expression RIGHT_PAREN
{ {
$$ = new elna::boot::cast_expression(elna::boot::make_position(@1), $5, $3); $$ = new elna::boot::cast_expression(elna::boot::make_position(@1), $5, $3);
} }
@ -403,9 +403,9 @@ optional_statements:
field_declaration: field_declaration:
IDENTIFIER COLON type_expression { $$ = std::make_pair($1, $3); } IDENTIFIER COLON type_expression { $$ = std::make_pair($1, $3); }
field_list: field_list:
field_declaration SEMICOLON field_list field_declaration field_list
{ {
std::swap($$, $3); std::swap($$, $2);
$$.emplace($$.cbegin(), $1); $$.emplace($$.cbegin(), $1);
} }
| field_declaration { $$.emplace_back($1); } | field_declaration { $$.emplace_back($1); }

View File

@ -43,6 +43,94 @@ namespace gcc
this->symbol_map = symbol_table; this->symbol_map = symbol_table;
} }
void generic_visitor::build_procedure_call(location_t call_location,
tree symbol, const std::vector<boot::expression *>& arguments)
{
vec<tree, va_gc> *argument_trees = nullptr;
tree current_parameter = TYPE_ARG_TYPES(TREE_TYPE(symbol));
vec_alloc(argument_trees, arguments.size());
for (boot::expression *const argument : arguments)
{
location_t argument_location = get_location(&argument->position());
if (is_void_type(TREE_VALUE(current_parameter)))
{
error_at(argument_location, "too many arguments, expected %i, got %lu",
list_length(TYPE_ARG_TYPES(TREE_TYPE(symbol))) - 1, arguments.size());
this->current_expression = error_mark_node;
break;
}
argument->accept(this);
if (!is_assignable_from(TREE_VALUE(current_parameter), this->current_expression))
{
error_at(argument_location,
"cannot assign value of type '%s' to variable of type '%s'",
print_type(TREE_TYPE(this->current_expression)).c_str(),
print_type(TREE_VALUE(current_parameter)).c_str());
this->current_expression = error_mark_node;
}
current_parameter = TREE_CHAIN(current_parameter);
argument_trees->quick_push(this->current_expression);
}
tree stmt = build_call_expr_loc_vec(call_location, symbol, argument_trees);
if (!is_void_type(TREE_VALUE(current_parameter)))
{
error_at(call_location, "too few arguments, expected %i, got %lu",
list_length(TYPE_ARG_TYPES(TREE_TYPE(symbol))) - 1, arguments.size());
this->current_expression = error_mark_node;
}
else if (TREE_TYPE(TREE_TYPE(symbol)) == void_type_node)
{
append_statement(stmt);
this->current_expression = NULL_TREE;
}
else
{
this->current_expression = stmt;
}
}
void generic_visitor::build_record_call(location_t call_location,
tree symbol, const std::vector<boot::expression *>& arguments)
{
vec<constructor_elt, va_gc> *tree_arguments = nullptr;
tree record_fields = TYPE_FIELDS(symbol);
for (boot::expression *const argument : arguments)
{
location_t argument_location = get_location(&argument->position());
if (is_void_type(record_fields))
{
error_at(argument_location, "too many arguments, expected %i, got %lu",
list_length(TYPE_FIELDS(symbol)), arguments.size());
this->current_expression = error_mark_node;
break;
}
argument->accept(this);
if (!is_assignable_from(TREE_TYPE(record_fields), this->current_expression))
{
error_at(argument_location,
"cannot assign value of type '%s' to variable of type '%s'",
print_type(TREE_TYPE(this->current_expression)).c_str(),
print_type(TREE_TYPE(record_fields)).c_str());
this->current_expression = error_mark_node;
}
CONSTRUCTOR_APPEND_ELT(tree_arguments, record_fields, this->current_expression);
record_fields = TREE_CHAIN(record_fields);
}
if (!is_void_type(record_fields))
{
error_at(call_location, "too few arguments, expected %i, got %lu",
list_length(TYPE_FIELDS(symbol)), arguments.size());
this->current_expression = error_mark_node;
}
else
{
this->current_expression = build_constructor(symbol, tree_arguments);
}
}
void generic_visitor::visit(boot::call_expression *expression) void generic_visitor::visit(boot::call_expression *expression)
{ {
tree symbol = this->lookup(expression->name()); tree symbol = this->lookup(expression->name());
@ -56,37 +144,11 @@ namespace gcc
} }
else if (DECL_P(symbol) && is_procedure_type(TREE_TYPE(symbol))) else if (DECL_P(symbol) && is_procedure_type(TREE_TYPE(symbol)))
{ {
vec<tree, va_gc> *arguments = nullptr; build_procedure_call(call_location, symbol, expression->arguments());
vec_alloc(arguments, expression->arguments().size());
for (boot::expression *const argument : expression->arguments())
{
argument->accept(this);
arguments->quick_push(this->current_expression);
}
tree stmt = build_call_expr_loc_vec(get_location(&expression->position()), symbol, arguments);
if (TREE_TYPE(TREE_TYPE(symbol)) == void_type_node)
{
append_statement(stmt);
this->current_expression = NULL_TREE;
}
else
{
this->current_expression = stmt;
}
} }
else if (TYPE_P(symbol) && is_record_type(symbol)) else if (TYPE_P(symbol) && is_record_type(symbol))
{ {
vec<constructor_elt, va_gc> *arguments = nullptr; build_record_call(call_location, symbol, expression->arguments());
tree record_fields = TYPE_FIELDS(symbol);
for (boot::expression *const argument : expression->arguments())
{
argument->accept(this);
CONSTRUCTOR_APPEND_ELT(arguments, record_fields, this->current_expression);
record_fields = TREE_CHAIN(record_fields);
}
this->current_expression = build_constructor(symbol, arguments);
} }
else else
{ {
@ -475,7 +537,9 @@ namespace gcc
} }
return; return;
} }
if (left_type != right_type && !are_compatible_pointers(left, right) && !are_compatible_pointers(right, left)) if (left_type != right_type
&& !are_compatible_pointers(left_type, right)
&& !are_compatible_pointers(right_type, left))
{ {
error_at(expression_location, error_at(expression_location,
"invalid operands of type %s and %s for operator %s", "invalid operands of type %s and %s for operator %s",
@ -848,8 +912,7 @@ namespace gcc
this->current_expression = error_mark_node; this->current_expression = error_mark_node;
return; return;
} }
if (TREE_TYPE(this->current_expression) == TREE_TYPE(lvalue) if (is_assignable_from(TREE_TYPE(lvalue), this->current_expression))
|| are_compatible_pointers(lvalue, this->current_expression))
{ {
tree assignment = build2_loc(statement_location, MODIFY_EXPR, tree assignment = build2_loc(statement_location, MODIFY_EXPR,
void_type_node, lvalue, this->current_expression); void_type_node, lvalue, this->current_expression);

View File

@ -67,15 +67,21 @@ namespace gcc
return TREE_CODE(type) == RECORD_TYPE; return TREE_CODE(type) == RECORD_TYPE;
} }
bool are_compatible_pointers(tree lhs, tree rhs) bool are_compatible_pointers(tree lhs_type, tree rhs)
{ {
tree lhs_type = TREE_TYPE(lhs); gcc_assert(TYPE_P(lhs_type));
tree rhs_type = TREE_TYPE(rhs); tree rhs_type = TREE_TYPE(rhs);
return (is_pointer_type(lhs_type) && rhs == elna_pointer_nil_node) return (is_pointer_type(lhs_type) && rhs == elna_pointer_nil_node)
|| (is_pointer_type(lhs_type) && lhs_type == rhs_type); || (is_pointer_type(lhs_type) && lhs_type == rhs_type);
} }
bool is_assignable_from(tree assignee, tree assignment)
{
return TREE_TYPE(assignment) == assignee
|| are_compatible_pointers(assignee, assignment);
}
void append_statement(tree statement_tree) void append_statement(tree statement_tree)
{ {
if (!vec_safe_is_empty(f_binding_level->defers)) if (!vec_safe_is_empty(f_binding_level->defers))

View File

@ -56,6 +56,10 @@ namespace gcc
tree build_logic_operation(boot::binary_expression *expression, tree build_logic_operation(boot::binary_expression *expression,
tree_code operator_code, tree left, tree right); tree_code operator_code, tree left, tree right);
tree build_equality_operation(boot::binary_expression *expression, tree left, tree right); tree build_equality_operation(boot::binary_expression *expression, tree left, tree right);
void build_procedure_call(location_t call_location,
tree symbol, const std::vector<boot::expression *>& arguments);
void build_record_call(location_t call_location,
tree symbol, const std::vector<boot::expression *>& arguments);
public: public:
generic_visitor(std::shared_ptr<boot::symbol_table<tree>> symbol_table); generic_visitor(std::shared_ptr<boot::symbol_table<tree>> symbol_table);

View File

@ -46,7 +46,14 @@ namespace gcc
* \param rhs Right hand value. * \param rhs Right hand value.
* \return Whether rhs can be assigned to lhs. * \return Whether rhs can be assigned to lhs.
*/ */
bool are_compatible_pointers(tree lhs, tree rhs); bool are_compatible_pointers(tree lhs_type, tree rhs);
/**
* \param assignee Assignee.
* \param assignee Assignment.
* \return Whether an expression assignment can be assigned to a variable of type assignee.
*/
bool is_assignable_from(tree assignee, tree assignment);
void append_statement(tree statement_tree); void append_statement(tree statement_tree);
void defer(tree statement_tree); void defer(tree statement_tree);

View File

@ -63,46 +63,46 @@ const
type type
Position* = record Position* = record
line: Word; line: Word
column: Word column: Word
end end
Location* = record Location* = record
first: Position; first: Position
last: Position last: Position
end end
SourceCode = record SourceCode = record
position: Position; position: Position
text: String text: String
end end
TokenValue* = union TokenValue* = union
int_value: Int; int_value: Int
string_value: pointer to Char; string_value: pointer to Char
string: String; string: String
boolean_value: Bool; boolean_value: Bool
char_value: Char char_value: Char
end end
Token* = record Token* = record
kind: Int; kind: Int
value: TokenValue; value: TokenValue
location: Location location: Location
end end
FILE* = record FILE* = record
dummy: Int dummy: Int
end end
CommandLine* = record CommandLine* = record
input: pointer to Char; input: pointer to Char
tokenize: Bool; tokenize: Bool
syntax_tree: Bool syntax_tree: Bool
end end
Literal* = record Literal* = record
value: Int value: Int
end end
ConstantDefinition* = record ConstantDefinition* = record
name: pointer to Char; name: pointer to Char
body: pointer to Literal body: pointer to Literal
end end
ConstantPart* = record ConstantPart* = record
elements: pointer to pointer to ConstantDefinition; elements: pointer to pointer to ConstantDefinition
count: Word count: Word
end end
Program* = record Program* = record
@ -148,12 +148,12 @@ end
proc write_s(value: String); proc write_s(value: String);
begin begin
write(0, value.ptr, value.length) write(0, cast(value.ptr: pointer to Byte), cast(value.length: Int))
end end
proc write_z(value: pointer to Char); proc write_z(value: pointer to Char);
begin begin
write(0, value, strlen(value)) write(0, cast(value: pointer to Byte), cast(strlen(value): Int))
end end
proc write_b(value: Bool); proc write_b(value: Bool);
@ -167,7 +167,7 @@ end
proc write_c(value: Char); proc write_c(value: Char);
begin begin
write(0, @value, 1) write(0, cast(@value: pointer to Byte), 1)
end end
proc write_i(value: Int); proc write_i(value: Int);
@ -184,7 +184,7 @@ begin
digit := value % 10; digit := value % 10;
value := value / 10; value := value / 10;
buffer[n] := cast(cast('0' as Int) + digit as Char); buffer[n] := cast(cast('0': Int) + digit: Char);
n := n - 1u n := n - 1u
end; end;
while n < 10u do while n < 10u do
@ -195,17 +195,17 @@ end
proc write_u(value: Word); proc write_u(value: Word);
begin begin
write_i(value) write_i(cast(value: Int))
end end
proc is_digit(c: Char) -> Bool; proc is_digit(c: Char) -> Bool;
begin begin
return cast(c as Int) >= cast('0' as Int) and cast(c as Int) <= cast('9' as Int) return cast(c: Int) >= cast('0': Int) and cast(c: Int) <= cast('9': Int)
end end
proc is_alpha(c: Char) -> Bool; proc is_alpha(c: Char) -> Bool;
begin begin
return cast(c as Int) >= cast('A' as Int) and cast(c as Int) <= cast('z' as Int) return cast(c: Int) >= cast('A': Int) and cast(c: Int) <= cast('z': Int)
end end
proc is_alnum(c: Char) -> Bool; proc is_alnum(c: Char) -> Bool;
@ -232,7 +232,7 @@ proc string_dup(origin: String) -> String;
var var
copy: pointer to Char; copy: pointer to Char;
begin begin
copy := cast(malloc(origin.length) as pointer to Char); copy := cast(malloc(origin.length): pointer to Char);
strncpy(copy, origin.ptr, origin.length); strncpy(copy, origin.ptr, origin.length);
return String(copy, origin.length) return String(copy, origin.length)
@ -246,9 +246,7 @@ proc make_position() -> Position;
var var
result: Position; result: Position;
begin begin
result.line := 1u; return Position(1u, 1u)
result.column := 1u;
return result
end end
proc read_source(filename: pointer to Char, result: pointer to String) -> Bool; proc read_source(filename: pointer to Char, result: pointer to String) -> Bool;
@ -274,12 +272,12 @@ begin
end; end;
rewind(input_file); rewind(input_file);
input := malloc(source_size); input := malloc(cast(source_size: Word));
if fread(input, source_size, 1, input_file) <> 1u then if fread(input, cast(source_size: Word), 1u, input_file) <> 1u then
return false return false
end; end;
result^.length := cast(source_size as Word); result^.length := cast(source_size: Word);
result^.ptr := cast(input as pointer to Char); result^.ptr := cast(input: pointer to Char);
return true return true
end end
@ -373,12 +371,12 @@ begin
while source_code^.text.length > 1u do while source_code^.text.length > 1u do
if source_code^.text[1u] = '*' and source_code^.text[2u] = ')' then if source_code^.text[1u] = '*' and source_code^.text[2u] = ')' then
source_code^ := advance_source(source_code^, 2u); source_code^ := advance_source(source_code^, 2u);
token_content^ := substring(token_content^, 0, content_length); token_content^ := substring(token_content^, 0u, content_length);
return true return true
end; end;
content_length := content_length + 1u; content_length := content_length + 1u;
source_code^ := advance_source(source_code^, 1) source_code^ := advance_source(source_code^, 1u)
end; end;
return false return false
@ -413,8 +411,8 @@ begin
if token_end^ <> '\"' then if token_end^ <> '\"' then
return input return input
end; end;
token_length := cast(token_end - input as Word); token_length := cast(token_end - input: Word);
current_token^.value.string_value := cast(calloc(token_length, 1) as pointer to Char); current_token^.value.string_value := cast(calloc(token_length, 1u): pointer to Char);
is_valid := true; is_valid := true;
constructed_string := current_token^.value.string_value; constructed_string := current_token^.value.string_value;
@ -565,7 +563,7 @@ begin
write_s("u>") write_s("u>")
elsif current_token^.kind = TOKEN_CHARACTER then elsif current_token^.kind = TOKEN_CHARACTER then
write_c('<'); write_c('<');
write_i(current_token^.value.char_value); write_i(cast(current_token^.value.char_value: Int));
write_s("c>") write_s("c>")
elsif current_token^.kind = TOKEN_STRING then elsif current_token^.kind = TOKEN_STRING then
write_s("\"...\"") write_s("\"...\"")
@ -671,7 +669,7 @@ begin
source_code := skip_spaces(source_code); source_code := skip_spaces(source_code);
while source_code.text.length <> 0u do while source_code.text.length <> 0u do
tokens := cast(reallocarray(tokens, tokens_size^ + 1u, sizeof(Token)) as pointer to Token); tokens := cast(reallocarray(cast(tokens: pointer to Byte), tokens_size^ + 1u, sizeof(Token)): pointer to Token);
current_token := tokens + tokens_size^; current_token := tokens + tokens_size^;
first_char := source_code.text[1u]; first_char := source_code.text[1u];
@ -681,7 +679,7 @@ begin
elsif is_digit(first_char) then elsif is_digit(first_char) then
token_end := nil; token_end := nil;
current_token^.value.int_value := strtol(source_code.text.ptr, @token_end, 10); current_token^.value.int_value := strtol(source_code.text.ptr, @token_end, 10);
token_length := cast(token_end - source_code.text.ptr as Word); token_length := cast(token_end - source_code.text.ptr: Word);
if token_end^ = 'u' then if token_end^ = 'u' then
current_token^.kind := TOKEN_WORD; current_token^.kind := TOKEN_WORD;
@ -712,7 +710,7 @@ begin
source_code := advance_source(source_code, 1u) source_code := advance_source(source_code, 1u)
elsif first_char = '\'' then elsif first_char = '\'' then
token_end := lex_character(source_code.text.ptr + 1, current_token); token_end := lex_character(source_code.text.ptr + 1, current_token);
token_length := cast(token_end - source_code.text.ptr as Word); token_length := cast(token_end - source_code.text.ptr: Word);
if token_end^ = '\'' then if token_end^ = '\'' then
current_token^.kind := TOKEN_CHARACTER; current_token^.kind := TOKEN_CHARACTER;
@ -725,7 +723,7 @@ begin
if token_end^ = '"' then if token_end^ = '"' then
current_token^.kind := TOKEN_STRING; current_token^.kind := TOKEN_STRING;
token_length := cast(token_end - source_code.text.ptr as Word); token_length := cast(token_end - source_code.text.ptr: Word);
source_code := advance_source(source_code, token_length + 1u) source_code := advance_source(source_code, token_length + 1u)
end end
elsif first_char = '[' then elsif first_char = '[' then
@ -823,7 +821,7 @@ end
proc parse_literal(tokens: pointer to pointer to Token, tokens_size: pointer to Word) -> pointer to Literal; proc parse_literal(tokens: pointer to pointer to Token, tokens_size: pointer to Word) -> pointer to Literal;
begin begin
return cast(calloc(1, sizeof(Literal)) as pointer to Literal) return cast(calloc(1u, sizeof(Literal)): pointer to Literal)
end end
proc parse_constant_definition(tokens: pointer to pointer to Token, proc parse_constant_definition(tokens: pointer to pointer to Token,
@ -831,9 +829,9 @@ proc parse_constant_definition(tokens: pointer to pointer to Token,
var var
result: pointer to ConstantDefinition; result: pointer to ConstantDefinition;
begin begin
result := cast(calloc(1, sizeof(ConstantDefinition)) as pointer to ConstantDefinition); result := cast(calloc(1u, sizeof(ConstantDefinition)): pointer to ConstantDefinition);
result^.name := cast(malloc(strlen(tokens^^.value.string_value)) as pointer to Char); result^.name := cast(malloc(strlen(tokens^^.value.string_value)): pointer to Char);
strcpy(result^.name, tokens^^.value.string_value); strcpy(result^.name, tokens^^.value.string_value);
tokens^ := tokens^ + 2u; tokens^ := tokens^ + 2u;
@ -855,7 +853,7 @@ var
result: pointer to Program, result: pointer to Program,
current_constant: pointer to pointer to ConstantDefinition; current_constant: pointer to pointer to ConstantDefinition;
begin begin
result := cast(calloc(1, sizeof(Program)) as pointer to Program); result := cast(calloc(1u, sizeof(Program)): pointer to Program);
result^.constants.elements := nil; result^.constants.elements := nil;
result^.constants.count := 0u; result^.constants.count := 0u;
@ -866,8 +864,11 @@ begin
while tokens_size^ > 0u and tokens^^.kind = TOKEN_IDENTIFIER do while tokens_size^ > 0u and tokens^^.kind = TOKEN_IDENTIFIER do
result^.constants.elements := cast( result^.constants.elements := cast(
reallocarray(result^.constants.elements, result^.constants.count + 1u, sizeof(pointer to ConstantDefinition)) reallocarray(
as pointer to pointer to ConstantDefinition); cast(result^.constants.elements: pointer to Byte),
result^.constants.count + 1u,
sizeof(pointer to ConstantDefinition)
) : pointer to pointer to ConstantDefinition);
current_constant := result^.constants.elements + result^.constants.count; current_constant := result^.constants.elements + result^.constants.count;
result^.constants.count := result^.constants.count + 1u; result^.constants.count := result^.constants.count + 1u;
@ -887,7 +888,7 @@ var
result: pointer to CommandLine; result: pointer to CommandLine;
begin begin
i := 1; i := 1;
result := cast(malloc(sizeof(CommandLine)) as pointer to CommandLine); result := cast(malloc(sizeof(CommandLine)): pointer to CommandLine);
result^.tokenize := false; result^.tokenize := false;
result^.syntax_tree := false; result^.syntax_tree := false;
result^.input := nil; result^.input := nil;
@ -950,5 +951,5 @@ begin
end end
begin begin
exit(process(count, parameters)) exit(process(cast(count: Int), cast(parameters: pointer to pointer to Char)))
end. end.