Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Compiler_Line_Frontend.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
52#ifndef COMPILER_LINE_FRONTEND_H
53#define COMPILER_LINE_FRONTEND_H
54
55#include <charconv>
56#include <cctype>
57#include <cstdlib>
58#include <sstream>
59#include <string>
60#include <string_view>
61#include <utility>
62
63#include <Compiler_Frontend.H>
69#include <Compiler_IR_Builder.H>
71
72namespace Aleph {
81{
82 enum class Operand_Kind
83 {
84 Integer,
86 };
87
95
108
110 {
111 enum class Kind
112 {
113 Integer,
114 Call
115 };
116
117 std::string module_name;
119 std::string name;
120 long long integer_value = 0;
121 std::string callee;
122 std::string callee_module_name;
127 };
128
137
143 size_t arena_bytes_ = 0;
144
156
159
160 static std::string trim_copy(std::string_view text)
161 {
162 size_t begin = 0;
163 while (begin < text.size() and std::isspace(static_cast<unsigned char>(text[begin])))
164 ++begin;
165
166 size_t end = text.size();
167 while (end > begin and std::isspace(static_cast<unsigned char>(text[end - 1])))
168 --end;
169
170 return std::string(text.substr(begin, end - begin));
171 }
172
173 static std::string trim_quotes_copy(std::string_view text)
174 {
175 if (text.size() >= 2 and text.front() == '"' and text.back() == '"')
176 return std::string(text.substr(1, text.size() - 2));
177 return std::string(text);
178 }
179
180 static DynArray<std::string> split_tokens(std::string_view text)
181 {
183 std::istringstream in{std::string(text)};
184 std::string token;
185 while (in >> token)
186 tokens.append(token);
187 return tokens;
188 }
189
190 static bool parse_integer(std::string_view text, long long &value) noexcept
191 {
192 if (text.empty())
193 return false;
194
195 const char *const begin = text.data();
196 const char *const finish = begin + text.size();
197 const auto result = std::from_chars(begin, finish, value);
198 return result.ec == std::errc{} and result.ptr == finish;
199 }
200
201 static bool is_identifier_start(const unsigned char ch) noexcept
202 {
203 return std::isalpha(ch) or ch == '_';
204 }
205
206 static bool is_identifier_continue(const unsigned char ch) noexcept
207 {
208 return std::isalnum(ch) or ch == '_';
209 }
210
211 static bool parse_identifier(std::string_view text) noexcept
212 {
213 if (text.empty() or not is_identifier_start(static_cast<unsigned char>(text.front())))
214 return false;
215
216 for (size_t i = 1; i < text.size(); ++i)
217 if (not is_identifier_continue(static_cast<unsigned char>(text[i])))
218 return false;
219
220 return true;
221 }
222
223 static Compiler_Operator_Kind parse_operator_name(std::string_view text) noexcept
224 {
225 if (text == "add")
227 if (text == "sub")
229 if (text == "mul")
231 if (text == "div")
234 }
235
236 void append_error(const std::string &text)
237 {
238 errors_.append(text);
239 }
240
241 void record_text_artifact(const std::string &label, std::string text)
242 {
244 }
245
246 void emit_error(const Source_Span &span, const std::string &code, const std::string &message)
247 {
249 }
250
251 size_t find_source_index(std::string_view name) const noexcept
252 {
253 for (size_t i = 0; i < source_names_.size(); ++i)
254 if (source_names_.access(i) == name)
255 return i;
256 return static_cast<size_t>(-1);
257 }
258
259 static bool parse_operand(std::string_view text, const Source_Span &span, Operand &out)
260 {
261 if (long long value = 0; parse_integer(text, value))
262 {
264 out.text = std::string(text);
265 out.integer_value = value;
266 out.span = span;
267 return true;
268 }
269
270 if (not parse_identifier(text))
271 return false;
272
274 out.text = std::string(text);
275 out.integer_value = 0;
276 out.span = span;
277 return true;
278 }
279
281 const DynArray<std::string> &tokens,
282 const Source_Span &line_span)
283 {
284 if (tokens.size() != 2)
285 {
286 emit_error(line_span, "LIN016", "expected 'import <source-name>'");
287 return false;
288 }
289
290 parsed.imports.append({trim_quotes_copy(tokens.access(1)), line_span});
291 return true;
292 }
293
295 const DynArray<std::string> &tokens,
296 const Source_Span &line_span)
297 {
298 if (tokens.size() != 8 or tokens.access(4) != "=")
299 {
300 emit_error(line_span, "LIN001", "expected 'func <name> <x> <y> = <op> <lhs> <rhs>'");
301 return false;
302 }
303
304 if (not parse_identifier(tokens.access(1)))
305 {
306 emit_error(line_span, "LIN023", "invalid function name '" + tokens.access(1) + "'");
307 return false;
308 }
309 if (not parse_identifier(tokens.access(2)))
310 {
311 emit_error(line_span, "LIN020", "invalid parameter name '" + tokens.access(2) + "'");
312 return false;
313 }
314 if (not parse_identifier(tokens.access(3)))
315 {
316 emit_error(line_span, "LIN021", "invalid parameter name '" + tokens.access(3) + "'");
317 return false;
318 }
319 if (tokens.access(2) == tokens.access(3))
320 {
321 emit_error(line_span, "LIN025", "duplicate parameter name '" + tokens.access(2) + "'");
322 return false;
323 }
324
326 def.module_name = parsed.name;
327 def.name = tokens.access(1);
328 def.left_param = tokens.access(2);
329 def.right_param = tokens.access(3);
330 def.op = parse_operator_name(tokens.access(5));
331 def.span = line_span;
332 def.name_span = line_span;
333
335 {
336 emit_error(line_span, "LIN002", "unknown line-frontend operator '" + tokens.access(5) + "'");
337 return false;
338 }
339
340 if (not parse_operand(tokens.access(6), line_span, def.lhs))
341 {
342 emit_error(line_span, "LIN018", "invalid function operand '" + tokens.access(6) + "'");
343 return false;
344 }
345
346 if (not parse_operand(tokens.access(7), line_span, def.rhs))
347 {
348 emit_error(line_span, "LIN018", "invalid function operand '" + tokens.access(7) + "'");
349 return false;
350 }
351
352 parsed.functions.append(std::move(def));
353 return true;
354 }
355
357 const DynArray<std::string> &tokens,
358 const Source_Span &line_span)
359 {
360 if (tokens.size() < 5 or tokens.access(2) != "=")
361 {
363 "LIN003",
364 "expected 'let <name> = int <value>' or 'let <name> = call <func> <a> <b>'");
365 return false;
366 }
367
368 if (not parse_identifier(tokens.access(1)))
369 {
370 emit_error(line_span, "LIN022", "invalid let binding name '" + tokens.access(1) + "'");
371 return false;
372 }
373
374 Let_Definition def;
375 def.module_name = parsed.name;
376 def.name = tokens.access(1);
377 def.span = line_span;
378 def.name_span = line_span;
379
380 if (tokens.access(3) == "int")
381 {
382 if (tokens.size() != 5)
383 {
384 emit_error(line_span, "LIN004", "expected 'let <name> = int <value>'");
385 return false;
386 }
387
388 long long value = 0;
389 if (not parse_integer(tokens.access(4), value))
390 {
391 emit_error(line_span, "LIN005", "invalid integer literal '" + tokens.access(4) + "'");
392 return false;
393 }
394
396 def.integer_value = value;
397 parsed.lets.append(std::move(def));
398 return true;
399 }
400
401 if (tokens.access(3) == "call")
402 {
403 if (tokens.size() != 7)
404 {
405 emit_error(line_span, "LIN006", "expected 'let <name> = call <func> <a> <b>'");
406 return false;
407 }
408
409 if (not parse_identifier(tokens.access(4)))
410 {
411 emit_error(line_span, "LIN024", "invalid callee name '" + tokens.access(4) + "'");
412 return false;
413 }
414
416 def.callee = tokens.access(4);
417 if (not parse_operand(tokens.access(5), line_span, def.arg1))
418 {
419 emit_error(line_span, "LIN019", "invalid call operand '" + tokens.access(5) + "'");
420 return false;
421 }
422
423 if (not parse_operand(tokens.access(6), line_span, def.arg2))
424 {
425 emit_error(line_span, "LIN019", "invalid call operand '" + tokens.access(6) + "'");
426 return false;
427 }
428
429 parsed.lets.append(std::move(def));
430 return true;
431 }
432
433 emit_error(line_span, "LIN007", "unknown let initializer kind '" + tokens.access(3) + "'");
434 return false;
435 }
436
438 {
439 const auto &text = sources_.file_text(parsed.file_id);
440 size_t line_begin = 0;
441 while (line_begin <= text.size())
442 {
443 const auto newline = text.find('\n', line_begin);
444 const auto raw_end = newline == std::string::npos ? text.size() : newline;
445 size_t content_end = raw_end;
446 if (content_end > line_begin and text[content_end - 1] == '\r')
447 --content_end;
448
449 std::string_view line(text.data() + line_begin, content_end - line_begin);
450 if (const auto comment = line.find('#'); comment != std::string_view::npos)
451 line = line.substr(0, comment);
452
453 const auto trimmed = trim_copy(line);
454 if (not trimmed.empty())
455 {
457 const auto tokens = split_tokens(trimmed);
458 if (not tokens.is_empty())
459 {
460 if (tokens.access(0) == "import")
462 else if (tokens.access(0) == "func")
463 (void) parse_function_line(parsed, tokens, line_span);
464 else if (tokens.access(0) == "let")
466 else
468 "LIN008",
469 "unknown line-frontend declaration '" + tokens.access(0) + "'");
470 }
471 }
472
473 if (newline == std::string::npos)
474 break;
475 line_begin = newline + 1;
476 }
477
478 return not diagnostics_.has_errors();
479 }
480
482 {
483 functions_.clear();
484 lets_.clear();
485
486 for (size_t i = 0; i < merge_order_.size(); ++i)
487 {
488 const auto &parsed = parsed_sources_.access(merge_order_.access(i));
489 for (size_t j = 0; j < parsed.functions.size(); ++j)
490 functions_.append(parsed.functions.access(j));
491 for (size_t j = 0; j < parsed.lets.size(); ++j)
492 lets_.append(parsed.lets.access(j));
493 }
494 }
495
501
503 {
505
507 for (size_t i = 0; i < result.issues.size(); ++i)
508 {
509 const auto &issue = result.issues.access(i);
511 emit_error(issue.span,
512 "LIN017",
513 "cannot resolve line-frontend import '" + issue.dependency_name + "'");
514 else
515 emit_error(issue.span,
516 "LIN018",
517 "cyclic line-frontend import involving '" + issue.module_name + "' and '"
518 + issue.dependency_name + "'");
519 }
520
521 for (size_t i = 0; i < result.order.size(); ++i)
522 merge_order_.append(result.order.access(i));
523
524 if (not result.valid and merge_order_.is_empty())
525 for (size_t i = 0; i < parsed_sources_.size(); ++i)
527
528 return result.valid and not diagnostics_.has_errors();
529 }
530
531 std::string dump_parsed_program() const
532 {
533 std::ostringstream out;
534 out << "LineProgram\n";
535 for (size_t i = 0; i < functions_.size(); ++i)
536 {
537 const auto &fn = functions_.access(i);
538 out << " func " << fn.name << " " << fn.left_param << " " << fn.right_param << " = "
539 << compiler_operator_name(fn.op) << " " << fn.lhs.text << " " << fn.rhs.text << '\n';
540 }
541 for (size_t i = 0; i < lets_.size(); ++i)
542 {
543 const auto &let = lets_.access(i);
544 out << " let " << let.name << " = ";
546 out << "int " << let.integer_value;
547 else
548 out << "call " << let.callee << " " << let.arg1.text << " " << let.arg2.text;
549 out << '\n';
550 }
551 return out.str();
552 }
553
554 std::string dump_semantic_state() const
555 {
556 std::ostringstream out;
557 out << "LineSema\n";
558 out << " Functions: " << functions_.size() << '\n';
559 out << " TopLevelLets: " << lets_.size() << '\n';
560 return out.str();
561 }
562
565 {
566 for (size_t i = 0; i < lets_.size(); ++i)
567 {
568 const auto &let = lets_.access(i);
570 continue;
571
572 if (const auto *value = compiler_lookup_module_value(env, let.module_name, let.callee);
574 {
575 lets_.access(i).callee_module_name = value->provider_module_name;
578 continue;
579 }
580
581 const auto resolution = compiler_resolve_module_name(table,
582 let.module_name,
583 let.callee,
587 diagnostics_, let.span, resolution, "LIN015", "line-frontend callee");
588 else
589 lets_.access(i).callee_module_name = resolution.binding.provider_module_name;
590
593 }
594
595 return not diagnostics_.has_errors();
596 }
597
599 const Function_Definition &function,
600 const Compiler_Symbol_Bindings &bindings)
601 {
602 if (operand.kind == Operand_Kind::Integer)
603 return true;
604
605 const auto *symbol_id = bindings.lookup(operand.text);
606 if (symbol_id != nullptr)
607 return true;
608
609 emit_error(operand.span,
610 "LIN009",
611 "unknown function-local name '" + operand.text + "' in '" + function.name + "'");
612 return false;
613 }
614
615 bool validate_top_level_operand(const Operand &operand, const Source_Span &span)
616 {
617 if (operand.kind == Operand_Kind::Integer)
618 return true;
619
620 emit_error(span,
621 "LIN010",
622 "top-level line frontend currently accepts only integer call arguments");
623 return false;
624 }
625
627 {
628 if (operand.kind == Operand_Kind::Integer)
629 return builder.make_integer_constant(operand.integer_value, types_.integer_type(), operand.span);
630 return builder.make_variable(operand.text, types_.integer_type(), operand.span);
631 }
632
634 Compiler_IR_Function &function,
635 const Compiler_IR_Block_Id block_id,
636 const Operand &operand,
639 const Function_Definition &definition) const
640 {
641 if (operand.kind == Operand_Kind::Integer)
642 return builder.emit_constant(function,
643 block_id,
645 std::to_string(operand.integer_value),
646 operand.span);
647
648 if (operand.text == definition.left_param)
649 return builder.emit_load_local(function, block_id, left_slot, types_.integer_type(), operand.span);
650 return builder.emit_load_local(function, block_id, right_slot, types_.integer_type(), operand.span);
651 }
652
653public:
659 explicit Compiler_Line_Frontend(const size_t arena_size = 1 << 20)
660 : diagnostics_(sources_), hir_ctx_(arena_size), ir_ctx_(arena_size), arena_bytes_(arena_size)
661 {}
662
668 [[nodiscard]] const char *name() const noexcept override
669 {
670 return "line";
671 }
672
680 void clear() noexcept override
681 {
684 module_descriptors_.clear();
685 module_metadata_.clear();
686 parsed_sources_.clear();
687 functions_.clear();
688 lets_.clear();
690 artifacts_.clear();
691 errors_.clear();
693 hir_module_ = nullptr;
694 ir_module_ = nullptr;
695
697 types_.clear();
698 hir_ctx_.reset();
699 ir_ctx_.reset();
701 }
702
711 {
712 if (inputs.is_empty())
713 {
714 append_error("line frontend: no input sources were provided");
715 return false;
716 }
717
718 for (size_t i = 0; i < inputs.size(); ++i)
719 {
720 const auto &input = inputs.access(i);
721 if (find_source_index(input.name) != static_cast<size_t>(-1))
722 {
723 append_error("line frontend: duplicate input source name '" + input.name + "'");
724 return false;
725 }
726
729 }
730
731 return true;
732 }
733
741 bool parse() override
742 {
743 functions_.clear();
744 lets_.clear();
745 module_descriptors_.clear();
746 module_metadata_.clear();
747 parsed_sources_.clear();
749
750 for (size_t i = 0; i < source_ids_.size(); ++i)
751 {
754 parsed.file_id = source_ids_.access(i);
756
758 descriptor.name = parsed.name;
759 for (size_t j = 0; j < parsed.imports.size(); ++j)
760 descriptor.imports.append(parsed.imports.access(j));
761 module_descriptors_.append(std::move(descriptor));
762
764 metadata.name = parsed.name;
765 for (size_t j = 0; j < parsed.functions.size(); ++j)
767 parsed.functions.access(j).name,
768 parsed.functions.access(j).name_span});
769 for (size_t j = 0; j < parsed.lets.size(); ++j)
771 parsed.lets.access(j).name,
772 parsed.lets.access(j).name_span});
773 module_metadata_.append(std::move(metadata));
774
775 parsed_sources_.append(std::move(parsed));
776 }
777
778 (void) resolve_import_order();
779 if (merge_order_.is_empty())
780 for (size_t i = 0; i < parsed_sources_.size(); ++i)
781 merge_order_.append(i);
782
783 flatten_program_in_import_order();
784 record_import_order_artifact();
785
786 record_text_artifact("line.parse", dump_parsed_program());
787 return not diagnostics_.has_errors();
788 }
789
797 bool analyze() override
798 {
799 for (size_t i = 0; i < parsed_sources_.size(); ++i)
800 {
801 const auto &module = parsed_sources_.access(i);
803 locals.enter_scope();
804
805 for (size_t j = 0; j < module.functions.size(); ++j)
806 {
807 const auto &fn = module.functions.access(j);
808 const auto result = locals.declare(Compiler_Symbol_Kind::Function, fn.name, fn.name_span);
809 if (result.duplicate_local())
810 emit_error(fn.name_span,
811 "LIN011",
812 "duplicate line-frontend function '" + fn.name + "'");
813 }
814
815 for (size_t j = 0; j < module.lets.size(); ++j)
816 {
817 const auto &let = module.lets.access(j);
818 const auto result = locals.declare(Compiler_Symbol_Kind::Global, let.name, let.name_span);
819 if (result.duplicate_local())
820 {
821 const auto &existing = locals.symbol(result.symbol_id);
822 if (existing.kind == Compiler_Symbol_Kind::Function)
823 emit_error(let.name_span,
824 "LIN014",
825 "top-level let '" + let.name + "' conflicts with a function name");
826 else
827 emit_error(let.name_span,
828 "LIN013",
829 "duplicate line-frontend top-level let '" + let.name + "'");
830 }
831 }
832 }
833
834 const auto linkage
835 = compiler_link_module_surfaces(module_descriptors_, module_metadata_, merge_order_);
836 const auto bindings = compiler_bind_module_surfaces(linkage);
837 const auto table = compiler_build_module_name_table(bindings);
839 (void) validate_top_level_calls(table, env);
840
841 for (size_t i = 0; i < functions_.size(); ++i)
842 {
843 const auto &fn = functions_.access(i);
845 locals.enter_scope();
846 (void) locals.declare(Compiler_Symbol_Kind::Parameter, fn.left_param, fn.span);
847 (void) locals.declare(Compiler_Symbol_Kind::Parameter, fn.right_param, fn.span);
848 (void) validate_operand_in_function(fn.lhs, fn, locals);
849 (void) validate_operand_in_function(fn.rhs, fn, locals);
850 }
851
852 record_text_artifact("line.sema", dump_semantic_state());
853 return not diagnostics_.has_errors();
854 }
855
863 bool lower_hir() override
864 {
866 hir_module_ = builder.make_module();
867 const auto function_type
868 = types_.make_function_type({types_.integer_type(), types_.integer_type()}, types_.integer_type());
869
870 for (size_t i = 0; i < functions_.size(); ++i)
871 {
872 const auto &fn = functions_.access(i);
873 auto *body = builder.make_block();
874 auto *function = builder.make_function(fn.name, function_type, fn.name_span, body, fn.span);
875 builder.add_parameter(*function, fn.left_param, types_.integer_type(), fn.span);
876 builder.add_parameter(*function, fn.right_param, types_.integer_type(), fn.span);
877 auto *result = builder.make_binary(build_hir_operand(builder, fn.lhs),
878 fn.op,
879 build_hir_operand(builder, fn.rhs),
880 types_.integer_type(),
881 fn.span);
882 builder.append_statement(*body, builder.make_return_stmt(result, fn.span));
883 builder.append_function(*hir_module_, function);
884 }
885
886 for (size_t i = 0; i < lets_.size(); ++i)
887 {
888 const auto &let = lets_.access(i);
889 if (let.kind == Let_Definition::Kind::Integer)
890 builder.append_top_level_statement(
891 *hir_module_,
892 builder.make_let_stmt(
893 let.name,
894 types_.integer_type(),
895 let.name_span,
896 builder.make_integer_constant(let.integer_value, types_.integer_type(), let.span),
897 let.span));
898 else
899 {
900 auto *call = builder.make_call(builder.make_variable(let.callee, function_type, let.span),
901 types_.integer_type(),
902 let.span,
903 let.span);
904 builder.append_argument(*call, build_hir_operand(builder, let.arg1));
905 builder.append_argument(*call, build_hir_operand(builder, let.arg2));
906 builder.append_top_level_statement(
907 *hir_module_,
908 builder.make_let_stmt(let.name, types_.integer_type(), let.name_span, call, let.span));
909 }
910 }
911
912 record_text_artifact("hir", compiler_dump_hir_module(hir_module_, types_));
913 return hir_module_ != nullptr;
914 }
915
923 bool lower_ir() override
924 {
926 ir_module_ = builder.make_module();
927 if (ir_module_ == nullptr)
928 {
929 append_error("ir: null IR module produced by line frontend");
930 return false;
931 }
932
933 const auto function_type
934 = types_.make_function_type({types_.integer_type(), types_.integer_type()}, types_.integer_type());
935
936 for (size_t i = 0; i < lets_.size(); ++i)
937 (void) builder.add_global_slot(*ir_module_,
938 lets_.access(i).name,
939 types_.integer_type(),
940 lets_.access(i).span);
941
943 for (size_t i = 0; i < functions_.size(); ++i)
944 {
945 const auto &fn = functions_.access(i);
946 auto *function = builder.make_function(fn.name, function_type, fn.span);
947 const auto id = builder.append_function(*ir_module_, function);
948 function_ids.append({fn.module_name + "::" + fn.name, id});
949 }
950
951 auto find_function_id = [&function_ids](std::string_view declaring_module,
952 std::string_view callee_name) noexcept
953 {
954 const std::string key = std::string(declaring_module) + "::" + std::string(callee_name);
955 for (size_t i = 0; i < function_ids.size(); ++i)
956 if (function_ids.access(i).first == key)
957 return function_ids.access(i).second;
958 return compiler_ir_invalid_id();
959 };
960
961 for (size_t i = 0; i < functions_.size(); ++i)
962 {
963 auto &function = *ir_module_->functions.access(i);
964 const auto &definition = functions_.access(i);
965 const auto left_slot = builder.add_local_slot(function,
966 Compiler_IR_Slot_Kind::Parameter,
967 definition.left_param,
968 types_.integer_type(),
969 definition.span);
970 const auto right_slot = builder.add_local_slot(function,
971 Compiler_IR_Slot_Kind::Parameter,
972 definition.right_param,
973 types_.integer_type(),
974 definition.span);
975 const auto entry = builder.create_block(function, "entry");
976 const auto exit = builder.create_block(function, "exit");
977 builder.set_entry_block(function, entry);
978 builder.set_exit_block(function, exit);
979
980 const auto lhs = build_ir_operand(
981 builder, function, entry, definition.lhs, left_slot, right_slot, definition);
982 const auto rhs = build_ir_operand(
983 builder, function, entry, definition.rhs, left_slot, right_slot, definition);
984 const auto result = builder.emit_binary(
985 function, entry, definition.op, lhs, rhs, types_.integer_type(), definition.span);
986 builder.set_return(function, entry, result, definition.span);
987 builder.set_exit(function, exit, definition.span);
988 }
989
990 if (not lets_.is_empty())
991 {
992 auto *top = builder.make_function("<top-level>");
993 builder.set_top_level(*ir_module_, top);
994 const auto entry = builder.create_block(*top, "entry");
995 const auto exit = builder.create_block(*top, "exit");
996 builder.set_entry_block(*top, entry);
997 builder.set_exit_block(*top, exit);
998
999 for (size_t i = 0; i < lets_.size(); ++i)
1000 {
1001 const auto &let = lets_.access(i);
1002 if (let.kind == Let_Definition::Kind::Integer)
1003 {
1004 const auto value = builder.emit_constant(
1005 *top, entry, types_.integer_type(), std::to_string(let.integer_value), let.span);
1006 builder.emit_store_global(*top, entry, i, value, types_.integer_type(), let.span);
1007 continue;
1008 }
1009
1011 arguments.append(builder.emit_constant(*top,
1012 entry,
1013 types_.integer_type(),
1014 std::to_string(let.arg1.integer_value),
1015 let.arg1.span));
1016 arguments.append(builder.emit_constant(*top,
1017 entry,
1018 types_.integer_type(),
1019 std::to_string(let.arg2.integer_value),
1020 let.arg2.span));
1021 const auto callee = builder.emit_function_ref(
1022 *top, entry, find_function_id(let.callee_module_name, let.callee), function_type, let.span);
1023 const auto value
1024 = builder.emit_call(*top, entry, callee, arguments, types_.integer_type(), let.span);
1025 builder.emit_store_global(*top, entry, i, value, types_.integer_type(), let.span);
1026 }
1027
1028 builder.set_jump(*top, entry, exit);
1029 builder.set_exit(*top, exit);
1030 }
1031
1032 record_text_artifact("ir", compiler_dump_ir_module(ir_module_, &types_));
1033
1034 const auto report = validate_ir_module(*ir_module_);
1035 return Compiler_Driver_Detail::merge_report("ir", report, errors_, warnings_);
1036 }
1037
1044 {
1045 return &diagnostics_;
1046 }
1047
1054 {
1055 return &diagnostics_;
1056 }
1057
1064 {
1065 return &types_;
1066 }
1067
1074 {
1075 return hir_module_;
1076 }
1077
1084 {
1085 return ir_module_;
1086 }
1087
1094 {
1095 return module_descriptors_;
1096 }
1097
1104 {
1105 return merge_order_;
1106 }
1107
1114 {
1115 return module_metadata_;
1116 }
1117
1125 {
1126 return artifacts_;
1127 }
1128
1135 [[nodiscard]] const Compiler_Driver_Artifact *find_artifact(std::string_view label) const noexcept override
1136 {
1137 for (size_t i = artifacts_.size(); i > 0; --i)
1138 if (artifacts_.access(i - 1).label == label)
1139 return &artifacts_.access(i - 1);
1140 return nullptr;
1141 }
1142
1149 {
1150 return errors_;
1151 }
1152
1159 {
1160 return warnings_;
1161 }
1162};
1163} // namespace Aleph
1164
1165#endif
Reusable interface for language frontends that plug into common drivers.
Reusable builder helpers for constructing HIR directly.
Reusable builder helpers for constructing explicit IR directly.
Reusable diagnostics for top-level inter-module name resolution.
Reusable top-level name lookup over bound module surfaces.
Reusable source-name module dependency resolution helpers.
Reusable namespace-separated semantic environment for modules.
Reusable lexical bindings and symbol records for frontends.
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Abstract language frontend that can feed reusable drivers.
Small reusable helper that allocates and connects HIR nodes.
Arena-backed ownership context for HIR nodes.
void reset() noexcept
Destroys all tracked objects and rewinds the arena.
Small reusable helper that allocates and connects IR nodes.
Arena-backed ownership context for IR nodes.
void reset() noexcept
Destroys all tracked objects and rewinds the arena.
Small example frontend for a line-oriented language.
bool validate_top_level_calls(const Compiler_Module_Name_Table &table, const Compiler_Module_Semantic_Environment &env)
DynArray< std::string > source_names_
static bool parse_operand(std::string_view text, const Source_Span &span, Operand &out)
static Compiler_Operator_Kind parse_operator_name(std::string_view text) noexcept
const DynArray< size_t > & module_merge_order() const noexcept override
Returns the dependency-resolved merge order from the last parse().
const Compiler_IR_Module * ir_module() const noexcept override
Returns the IR module produced by the last lower_ir() call.
DynArray< Parsed_Source > parsed_sources_
bool parse() override
Parses all registered sources, builds module descriptors and metadata, resolves import order,...
DynArray< Compiler_Driver_Artifact > artifacts_
bool parse_let_line(Parsed_Source &parsed, const DynArray< std::string > &tokens, const Source_Span &line_span)
bool analyze() override
Performs semantic validation: duplicate declarations, module linking, name binding,...
const Compiler_Type_Context * type_context() const noexcept override
Returns the shared type context used by the line frontend.
Diagnostic_Engine * diagnostic_engine() noexcept override
Returns the mutable diagnostic engine for this frontend.
const Compiler_HIR_Module * hir_module() const noexcept override
Returns the HIR module produced by the last lower_hir() call.
Compiler_IR_Value_Id build_ir_operand(Compiler_IR_Builder &builder, Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Operand &operand, const Compiler_IR_Local_Slot_Id left_slot, const Compiler_IR_Local_Slot_Id right_slot, const Function_Definition &definition) const
static std::string trim_copy(std::string_view text)
bool parse_function_line(Parsed_Source &parsed, const DynArray< std::string > &tokens, const Source_Span &line_span)
const DynArray< std::string > & errors() const noexcept override
Returns hard frontend-level errors collected outside the diagnostic engine.
DynArray< Function_Definition > functions_
DynArray< Compiler_Module_Descriptor > module_descriptors_
size_t find_source_index(std::string_view name) const noexcept
bool validate_operand_in_function(const Operand &operand, const Function_Definition &function, const Compiler_Symbol_Bindings &bindings)
const DynArray< Compiler_Driver_Artifact > & artifacts() const noexcept override
Returns all frontend-produced artifacts from the last run.
void record_text_artifact(const std::string &label, std::string text)
bool parse_import_line(Parsed_Source &parsed, const DynArray< std::string > &tokens, const Source_Span &line_span)
DynArray< Source_File_Id > source_ids_
static std::string trim_quotes_copy(std::string_view text)
static bool is_identifier_continue(const unsigned char ch) noexcept
bool lower_ir() override
Lowers the analyzed line program into a reusable flat IR module.
const DynArray< Compiler_Module_Metadata > & module_metadata() const noexcept override
Returns the module metadata built during parse().
const DynArray< std::string > & warnings() const noexcept override
Returns non-fatal frontend-level warnings.
Compiler_HIR_Expr * build_hir_operand(Compiler_HIR_Builder &builder, const Operand &operand) const
Compiler_Line_Frontend(const size_t arena_size=1<< 20)
Constructs a line-oriented frontend with reusable HIR/IR arena storage.
void append_error(const std::string &text)
static bool is_identifier_start(const unsigned char ch) noexcept
DynArray< Compiler_Module_Metadata > module_metadata_
bool parse_source_file(Parsed_Source &parsed)
static bool parse_identifier(std::string_view text) noexcept
bool validate_top_level_operand(const Operand &operand, const Source_Span &span)
DynArray< Let_Definition > lets_
static DynArray< std::string > split_tokens(std::string_view text)
void clear() noexcept override
Resets all frontend-owned state so the instance can be reused.
bool load_sources(const DynArray< Compiler_Driver_Source > &inputs) override
Registers ordered in-memory sources for the next compilation run.
void emit_error(const Source_Span &span, const std::string &code, const std::string &message)
const DynArray< Compiler_Module_Descriptor > & module_descriptors() const noexcept override
Returns the module descriptors built during parse().
bool lower_hir() override
Lowers the analyzed line program into a reusable HIR module.
static bool parse_integer(std::string_view text, long long &value) noexcept
const Diagnostic_Engine * diagnostic_engine() const noexcept override
Returns the read-only diagnostic engine for this frontend.
const Compiler_Driver_Artifact * find_artifact(std::string_view label) const noexcept override
Finds one frontend artifact by exact label (last-write wins).
const char * name() const noexcept override
Returns the stable frontend name.
Reusable lexical symbol table with stable ids and scope tracking.
Compiler_Symbol_Declare_Result declare(const Compiler_Symbol_Kind kind, const std::string &name, const Source_Span &span)
Declares one symbol in the current scope.
const Compiler_Symbol & symbol(const Compiler_Symbol_Id id) const
Returns one symbol by its stable 1-based identifier.
const Compiler_Symbol_Id * lookup(const std::string &name) const noexcept
Looks up one visible symbol id by name.
size_t enter_scope()
Enters one nested lexical scope.
Context owning all compiler type nodes.
Compiler_Type_Id make_function_type(const DynArray< Compiler_Type_Id > &parameters, const Compiler_Type_Id result)
Creates a function type.
void clear() noexcept
Drops user-created types and keeps the built-ins.
Compiler_Type_Id integer_type() const noexcept
Returns the preloaded Int type id.
Diagnostic_Builder & code(const std::string &value)
Sets the stable diagnostic code.
size_t emit() const noexcept
Finalizes the builder and returns the diagnostic index.
Diagnostic accumulator and renderer.
void clear() noexcept
Removes all accumulated diagnostics and resets counters.
bool has_errors() const noexcept
Returns whether any error or fatal diagnostic was emitted.
Diagnostic_Builder error(const Source_Span &span, const std::string &msg)
Starts an error diagnostic.
void clear() noexcept
Empties the container.
size_t size() const noexcept
Return the current dimension of array.
T & access(const size_t i) const noexcept
Fast access without checking allocation and bound_min_clock checking.
T & append()
Allocate a new entry to the end of array.
bool is_empty() const noexcept
Return true if the array is empty.
Stores source files and resolves offsets into human-readable data.
Definition ah-source.H:184
const std::string & file_text(const Source_File_Id id) const
Returns the full text of file id.
Definition ah-source.H:300
Source_File_Id add_virtual_file(const std::string &name, const std::string &text)
Registers an in-memory source file.
Definition ah-source.H:258
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
bool merge_report(const std::string &stage, const Report &report, DynArray< std::string > &errors, DynArray< std::string > &warnings)
void append_artifact(DynArray< Compiler_Driver_Artifact > &artifacts, const std::string &label, std::string text)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Compiler_Module_Semantic_Environment compiler_build_module_semantic_environment(const Compiler_Module_Name_Table &table)
Builds one namespace-separated semantic environment from a name table.
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:95
std::string compiler_dump_ir_module(const Compiler_IR_Module *module, const Compiler_Type_Context *types=nullptr)
Dumps all lowered IR in one module deterministically.
const char * compiler_operator_name(const Compiler_Operator_Kind kind) noexcept
Returns a stable debug name for one operator kind.
Compiler_IR_Validation_Report validate_ir_module(const Compiler_IR_Module &module)
Validates all functions in one lowered IR module.
void exit(const char *file, int line, const char *format,...)
Print a message and exit the program.
Definition ahDefs.C:125
and
Check uniqueness with explicit hash + equality functors.
std::string compiler_render_module_order(const DynArray< size_t > &order, const DynArray< Compiler_Module_Descriptor > &modules)
Renders one deterministic text artifact for a module order.
Compiler_Module_Resolution_Result compiler_resolve_module_order(const DynArray< Compiler_Module_Descriptor > &modules)
Resolves one dependency-first order for exact-name module imports.
Compiler_Module_Name_Table compiler_build_module_name_table(const Compiler_Module_Binding_Result &bindings)
Builds one reusable per-module name table from bindings.
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
size_t compiler_emit_module_name_resolution_diagnostic(Diagnostic_Engine &diagnostics, const Source_Span &span, const Compiler_Module_Name_Resolution &resolution, const std::string &code, const std::string &subject)
Emits one structured diagnostic for a module-name resolution failure.
size_t Compiler_IR_Value_Id
constexpr size_t compiler_ir_invalid_id() noexcept
Returns the sentinel invalid IR id.
std::string compiler_dump_hir_module(const Compiler_HIR_Module *module, const Compiler_Type_Context &types)
Dumps one HIR module in a deterministic text format.
const Compiler_Module_Bound_Name * compiler_lookup_module_value(const Compiler_Module_Semantic_Environment &env, const std::string_view module_name, const std::string_view symbol_name) noexcept
Looks up one visible value-namespace binding in a module environment.
size_t Compiler_IR_Block_Id
Compiler_Module_Name_Resolution compiler_resolve_module_name(const Compiler_Module_Binding_Result &bindings, const std::string_view module_name, const std::string_view symbol_name, const Compiler_Module_Name_Filter filter=Compiler_Module_Name_Filter::Any)
Resolves one top-level name through reusable per-module bindings.
Compiler_Module_Link_Result compiler_link_module_surfaces(const DynArray< Compiler_Module_Descriptor > &descriptors, const DynArray< Compiler_Module_Metadata > &metadata, const DynArray< size_t > &order)
Links direct-import module surfaces into visible top-level views.
Compiler_Module_Binding_Result compiler_bind_module_surfaces(const Compiler_Module_Link_Result &linkage)
Binds linked module surfaces into one deterministic name view.
size_t Source_File_Id
Definition ah-source.H:61
Compiler_Operator_Kind
Stable operator kinds shared by reusable compiler layers.
size_t Compiler_IR_Local_Slot_Id
Stable text artifact produced during one pipeline run.
Base class for HIR expressions.
Lowered IR for one function or top-level body.
Lowered IR module with shared global slots and functions.
DynArray< Compiler_IR_Function * > functions
Lowered non-top-level functions.
std::string callee_module_name
Resolved declaring module of callee (populated during analysis).
DynArray< Compiler_Module_Dependency > imports
One module descriptor consumed by the reusable resolver.
std::string name
Stable module/source name.
Reusable metadata describing one parsed or analyzed module surface.
std::string name
Stable module/source name.
Cached top-level name table over all visible modules.
Semantic environments for all modules in one compilation unit set.
Half-open byte range inside a source file.
Definition ah-source.H:100
Source_File_Id file_id
Stable file identifier; 0 means invalid.
Definition ah-source.H:101
long double resolution