├── .gitignore ├── runtime ├── CMakeLists.txt └── vm │ ├── src │ ├── actor │ │ ├── actor_type.cpp │ │ ├── actor_system.h │ │ ├── actor_type.h │ │ ├── actor_system.cpp │ │ ├── actor.cpp │ │ ├── actor.h │ │ └── actor.test.cpp │ ├── core │ │ ├── address.test.cpp │ │ ├── address.cpp │ │ └── address.h │ ├── concurrency │ │ ├── spin_lock.h │ │ └── spin_lock.cpp │ ├── mailbox │ │ ├── fiber.h │ │ ├── fiber.cpp │ │ ├── mailbox.cpp │ │ ├── mailbox.test.cpp │ │ ├── mailbox.h │ │ └── fiber.test.cpp │ ├── api.h │ └── api.cpp │ └── CMakeLists.txt ├── sn_examples ├── playground │ ├── extern │ │ ├── printbb.h │ │ └── printbb.c │ └── sample.sn └── capabilities │ └── capabilities.sn ├── common ├── CMakeLists.txt └── src │ ├── metadata.cpp │ └── metadata.h ├── ast ├── src │ ├── nidentifier.cpp │ ├── ntype.cpp │ ├── ast.h │ ├── ntype.h │ ├── nidentifier.h │ ├── nspawn_entity.h │ ├── nmethod_call.h │ ├── nconstant.h │ ├── nspawn_entity.cpp │ ├── nmethod_call.cpp │ ├── ir │ │ ├── nstruct.cpp │ │ └── nstruct.h │ ├── nconstant.cpp │ ├── nfunction_call.h │ ├── nfunction_call.cpp │ ├── nlet.h │ ├── nclass.h │ ├── nlet.cpp │ ├── node.h │ ├── nclass.cpp │ └── node.cpp └── CMakeLists.txt ├── discovery ├── CMakeLists.txt └── src │ ├── discovery.h │ └── discovery.cpp ├── lexer ├── src │ ├── lexer.spec.cpp │ ├── lexer.h │ ├── token.h │ ├── token_type.h │ ├── token_type.cpp │ └── lexer.test.cpp └── CMakeLists.txt ├── parser ├── src │ ├── parser.spec.cpp │ ├── parser.h │ ├── parser_method_call.test.cpp │ ├── parser_type.test.cpp │ ├── parser_spawn.test.cpp │ ├── parser_function_call.test.cpp │ ├── parser_let.test.cpp │ ├── parser_class.test.cpp │ └── test-includes │ │ └── includes.hpp └── CMakeLists.txt ├── passes ├── src │ ├── pass.h │ ├── pass_manager.h │ ├── analyzers │ │ ├── pass_detect_classes.h │ │ ├── pass_internal_modeler.cpp │ │ ├── pass_internal_modeler.h │ │ └── pass_detect_classes.cpp │ ├── mutations │ │ ├── pass_value_class_ir_transformer.h │ │ ├── pass_entity_class_method_resolution.h │ │ ├── pass_value_class_ir_transformer.cpp │ │ ├── pass_entity_class_ir_transformer.h │ │ ├── pass_entity_class_method_resolution.cpp │ │ └── pass_entity_class_ir_transformer.cpp │ └── pass_manager.cpp └── CMakeLists.txt ├── type-system ├── src │ ├── type-system.spec.cpp │ ├── type_registry.h │ ├── memory │ │ ├── internal_modeler.h │ │ ├── internal_modeler.test.cpp │ │ └── internal_modeler.cpp │ ├── type_registry.cpp │ ├── type_registry.test.cpp │ └── type.h └── CMakeLists.txt ├── backend-llvm ├── src │ ├── intrinsics │ │ └── intrinsics.h │ ├── backend.h │ ├── ir_builder.h │ ├── backend.cpp │ └── runtime │ │ └── runtime.h └── CMakeLists.txt ├── diagnostic ├── CMakeLists.txt └── src │ ├── diagnostic.h │ └── diagnostic.cpp ├── plot-diagnostic-file.sh ├── README.md ├── CMakeLists.txt ├── compile-and-run-playground.sh ├── LICENSE ├── documentation ├── welcome │ └── 0-hello-world.md └── advanced │ └── memory-model │ ├── 0-memory-layout-isolation.md │ ├── img │ └── 0-memory-model-boundaries-ABI.svg │ └── 1-memory-model.md ├── .github └── workflows │ └── health.yml ├── main.cpp └── CODE_OF_CONDUCT.md /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-build-debug 2 | /cmake-build-release 3 | /cmake-build 4 | /.idea 5 | *.o 6 | *.out 7 | *.json 8 | /perf.data 9 | /perf.data.old -------------------------------------------------------------------------------- /runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(runtime) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_subdirectory(vm) 7 | -------------------------------------------------------------------------------- /sn_examples/playground/extern/printbb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void printbb(unsigned int *bb, char *b1, char *b2, char *b3, char *b4, char *b5, char *b6, char *b7, char *b8); 4 | -------------------------------------------------------------------------------- /common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(common) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_library(common src/metadata.cpp src/metadata.h) 7 | -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor_type.cpp: -------------------------------------------------------------------------------- 1 | #include "actor_type.h" 2 | 3 | using namespace vm::actor; 4 | 5 | dispatch_message actor_type::resolve(std::string message_name) { 6 | return dispatch_table[message_name]; 7 | } 8 | -------------------------------------------------------------------------------- /runtime/vm/src/core/address.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "address.h" 4 | 5 | TEST(address, are_unique) { 6 | auto addr1 = vm::core::make_address(); 7 | auto addr2 = vm::core::make_address(); 8 | ASSERT_FALSE(addr1.id == addr2.id); 9 | } -------------------------------------------------------------------------------- /runtime/vm/src/core/address.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "address.h" 3 | 4 | using namespace vm::core; 5 | 6 | static std::random_device rd; 7 | static std::mt19937_64 gen(rd()); 8 | 9 | address vm::core::make_address() { 10 | return { .id = gen() }; 11 | } -------------------------------------------------------------------------------- /ast/src/nidentifier.cpp: -------------------------------------------------------------------------------- 1 | #include "nidentifier.h" 2 | 3 | 4 | namespace scc::ast { 5 | ast::nidentifier::nidentifier() = default; 6 | ast::nidentifier::~nidentifier() = default; 7 | 8 | void nidentifier::to_json(json &j) { 9 | j["name"] = name; 10 | } 11 | } -------------------------------------------------------------------------------- /ast/src/ntype.cpp: -------------------------------------------------------------------------------- 1 | #include "ntype.h" 2 | 3 | namespace scc::ast { 4 | ntype::ntype() = default; 5 | ntype::~ntype() = default; 6 | 7 | void ntype::to_json(json &j) { 8 | j["name"] = this->name; 9 | j["constraints"] = this->constraints; 10 | } 11 | } -------------------------------------------------------------------------------- /ast/src/ast.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node.h" 4 | #include "nclass.h" 5 | #include "nconstant.h" 6 | #include "nfunction_call.h" 7 | #include "nidentifier.h" 8 | #include "nlet.h" 9 | #include "nmethod_call.h" 10 | #include "nspawn_entity.h" 11 | #include "ntype.h" 12 | 13 | #include "ir/nstruct.h" -------------------------------------------------------------------------------- /ast/src/ntype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node.h" 4 | 5 | namespace scc::ast { 6 | struct ntype : public node { 7 | ntype(); 8 | ~ntype(); 9 | 10 | string name; 11 | type_constraints constraints; 12 | 13 | void to_json(json& j) override; 14 | }; 15 | } -------------------------------------------------------------------------------- /discovery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(discovery) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include_directories(../diagnostic/src) 7 | add_library(discovery src/discovery.cpp src/discovery.h) 8 | 9 | enable_testing() 10 | find_package(GTest REQUIRED) 11 | include(GoogleTest) -------------------------------------------------------------------------------- /sn_examples/capabilities/capabilities.sn: -------------------------------------------------------------------------------- 1 | capability class IO(allowed: Boolean, path: String = '/') 2 | 3 | entity class File(path: String) requires (io: IO) { 4 | require io.allowed && Path(io.path).parentOf(path) 5 | } 6 | 7 | entity class Service(file: File) 8 | 9 | let x = spawn Service('/tmp/example') with IO(path='/tmp') -------------------------------------------------------------------------------- /lexer/src/lexer.spec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "diagnostic.h" 3 | 4 | int main(int argc, char **argv) { 5 | testing::InitGoogleTest(&argc, argv); 6 | auto result = RUN_ALL_TESTS(); 7 | if (result != 0) { 8 | scc::diagnostic::dump_diagnostic("lexer-diagnostic.json"); 9 | } 10 | return result; 11 | } -------------------------------------------------------------------------------- /parser/src/parser.spec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "diagnostic.h" 3 | 4 | int main(int argc, char **argv) { 5 | testing::InitGoogleTest(&argc, argv); 6 | auto result = RUN_ALL_TESTS(); 7 | if (result != 0) { 8 | scc::diagnostic::dump_diagnostic("parser-diagnostic.json"); 9 | } 10 | return result; 11 | } -------------------------------------------------------------------------------- /passes/src/pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "diagnostic.h" 4 | #include "ast.h" 5 | 6 | namespace scc::passes { 7 | class pass { 8 | public: 9 | virtual ~pass() = default; 10 | virtual void execute(scc::ast::ast_root &root) const = 0; 11 | virtual diagnostic::diagnostic_phase_id pass_phase() const = 0; 12 | }; 13 | } -------------------------------------------------------------------------------- /type-system/src/type-system.spec.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "diagnostic.h" 3 | 4 | int main(int argc, char **argv) { 5 | testing::InitGoogleTest(&argc, argv); 6 | auto result = RUN_ALL_TESTS(); 7 | if (result != 0) { 8 | scc::diagnostic::dump_diagnostic("type-system-diagnostic.json"); 9 | } 10 | return result; 11 | } -------------------------------------------------------------------------------- /parser/src/parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node.h" 4 | #include "lexer.h" 5 | 6 | namespace scc::parser { 7 | using namespace scc::ast; 8 | using namespace scc::lexer; 9 | 10 | class parser { 11 | public: 12 | explicit parser(); 13 | ~parser(); 14 | 15 | ast_root parse(const token_stream &tokens); 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /discovery/src/discovery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace scc::discovery { 7 | using std::string; 8 | using std::list; 9 | 10 | class discovery { 11 | public: 12 | explicit discovery(); 13 | ~discovery(); 14 | 15 | list discover_source_files(const list &directories); 16 | }; 17 | } -------------------------------------------------------------------------------- /backend-llvm/src/intrinsics/intrinsics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "llvm/IR/LLVMContext.h" 4 | #include "llvm/IR/Module.h" 5 | 6 | namespace scc::backend::llvm::intrinsics { 7 | using namespace ::llvm; 8 | using std::shared_ptr; 9 | 10 | class intrinsics { 11 | public: 12 | virtual void register_into(shared_ptr &context, shared_ptr &module) = 0; 13 | }; 14 | } -------------------------------------------------------------------------------- /ast/src/nidentifier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "metadata.h" 7 | #include "node.h" 8 | 9 | namespace scc::ast { 10 | using std::variant; 11 | using namespace scc::common; 12 | 13 | struct nidentifier : public expression { 14 | nidentifier(); 15 | ~nidentifier(); 16 | 17 | string name; 18 | 19 | void to_json(json& j) override; 20 | }; 21 | } -------------------------------------------------------------------------------- /passes/src/pass_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "pass.h" 5 | #include "type_registry.h" 6 | 7 | namespace scc::passes { 8 | class pass_manager { 9 | public: 10 | explicit pass_manager(std::shared_ptr &types); 11 | void run(scc::ast::ast_root &root); 12 | private: 13 | std::list> validations; 14 | std::list> mutations; 15 | }; 16 | } -------------------------------------------------------------------------------- /diagnostic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(diagnostic) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | find_package(nlohmann_json 3.2.0 REQUIRED) 7 | 8 | add_library(diagnostic src/diagnostic.cpp src/diagnostic.h) 9 | 10 | enable_testing() 11 | find_package(GTest REQUIRED) 12 | include(GoogleTest) 13 | 14 | add_executable(diagnostic_tests src/diagnostic.cpp) 15 | target_link_libraries(diagnostic_tests GTest::GTest GTest::Main) 16 | gtest_discover_tests(diagnostic_tests) -------------------------------------------------------------------------------- /runtime/vm/src/core/address.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace vm::core { 7 | struct address { 8 | uint64_t id; 9 | }; 10 | 11 | address make_address(); 12 | } 13 | 14 | namespace std 15 | { 16 | template<> struct less 17 | { 18 | bool operator() (const vm::core::address& lhs, const vm::core::address& rhs) const 19 | { 20 | return lhs.id < rhs.id; 21 | } 22 | }; 23 | } -------------------------------------------------------------------------------- /type-system/src/type_registry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace scc::type_system { 10 | class type_registry { 11 | public: 12 | explicit type_registry(); 13 | ~type_registry(); 14 | 15 | std::shared_ptr resolve(const std::string &name); 16 | bool defined(const std::string &name); 17 | std::list> all_types(); 18 | private: 19 | std::map> map; 20 | }; 21 | 22 | } -------------------------------------------------------------------------------- /lexer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(lexer) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include_directories(../diagnostic/src) 7 | include_directories(../common/src) 8 | 9 | add_library(lexer src/token_type.h src/token.h src/lexer.cpp src/lexer.h src/token_type.cpp) 10 | 11 | enable_testing() 12 | find_package(GTest REQUIRED) 13 | include(GoogleTest) 14 | 15 | add_executable(lexer_tests src/lexer.spec.cpp src/lexer.test.cpp) 16 | target_link_libraries(lexer_tests lexer GTest::GTest GTest::Main diagnostic) 17 | gtest_discover_tests(lexer_tests) -------------------------------------------------------------------------------- /sn_examples/playground/sample.sn: -------------------------------------------------------------------------------- 1 | let extern printf(text: string, value1: boolean, value2: boolean): nothing 2 | let extern printbb(bb: any, b1: string, b2: string, b3: string, b4: string, b5: string, b6: string, b7: string, b8: string): nothing 3 | 4 | entity class EntityTest(first: boolean, second: boolean) { 5 | let do1() = self.first = true 6 | let do2() = self.second = true 7 | 8 | let print() = printbb(self, 'first', 'second', '*', '*', '*', '*', '*', '*') 9 | } 10 | 11 | let x = spawn EntityTest() 12 | x.print() 13 | x.do1() 14 | x.print() 15 | x.do2() 16 | x.print() 17 | -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor_system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../concurrency/spin_lock.h" 7 | #include "actor.h" 8 | 9 | namespace vm::actor { 10 | class actor_system { 11 | public: 12 | actor_system(); 13 | ~actor_system(); 14 | 15 | std::shared_ptr resolve_by_address(const address &addr); 16 | void register_actor(const std::shared_ptr& actor_to_register); 17 | private: 18 | std::map> registry; 19 | concurrency::spin_lock lock; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /lexer/src/lexer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "token.h" 8 | 9 | namespace scc::lexer { 10 | using std::istream; 11 | using std::shared_ptr; 12 | using std::unique_ptr; 13 | using std::list; 14 | 15 | typedef list> token_stream; 16 | typedef list>::const_iterator token_stream_iterator; 17 | 18 | class lexer { 19 | public: 20 | explicit lexer(); 21 | ~lexer(); 22 | 23 | token_stream process(std::istream &istream, const std::string &name); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /passes/src/analyzers/pass_detect_classes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../pass.h" 4 | #include "type_registry.h" 5 | 6 | namespace scc::passes::analyzers { 7 | using namespace scc::type_system; 8 | 9 | class pass_detect_classes : public pass { 10 | public: 11 | explicit pass_detect_classes(const std::shared_ptr &types); 12 | ~pass_detect_classes() override; 13 | 14 | void execute(scc::ast::ast_root &root) const override; 15 | diagnostic::diagnostic_phase_id pass_phase() const override; 16 | 17 | private: 18 | const std::shared_ptr types; 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../mailbox/mailbox.h" 8 | #include "actor.h" 9 | 10 | namespace vm::actor { 11 | using vm::mailbox::message; 12 | enum actor_message_process_result; 13 | class actor; 14 | 15 | typedef std::function)> dispatch_message; 16 | 17 | struct actor_type { 18 | dispatch_message resolve(std::string message_name); 19 | 20 | std::string class_name; 21 | std::map dispatch_table; 22 | }; 23 | } -------------------------------------------------------------------------------- /ast/src/nspawn_entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "metadata.h" 8 | #include "node.h" 9 | #include "nfunction_call.h" 10 | 11 | namespace scc::ast { 12 | using std::list; 13 | using std::variant; 14 | using std::shared_ptr; 15 | 16 | using namespace scc::common; 17 | 18 | struct nspawn_entity : public expression { 19 | nspawn_entity(); 20 | ~nspawn_entity(); 21 | 22 | std::string entity_name; 23 | list> arguments; 24 | 25 | void to_json(json &j) override; 26 | }; 27 | } -------------------------------------------------------------------------------- /passes/src/analyzers/pass_internal_modeler.cpp: -------------------------------------------------------------------------------- 1 | #include "pass_internal_modeler.h" 2 | 3 | namespace scc::passes::analyzers { 4 | 5 | pass_internal_modeler::pass_internal_modeler(const std::shared_ptr &modeler) 6 | : modeler(modeler) { 7 | 8 | } 9 | 10 | pass_internal_modeler::~pass_internal_modeler() = default; 11 | 12 | void pass_internal_modeler::execute(ast::ast_root &root) const { 13 | modeler->model_all_types(); 14 | } 15 | 16 | diagnostic::diagnostic_phase_id pass_internal_modeler::pass_phase() const { 17 | return diagnostic::diagnostic_phase_id::PASS_INTERNAL_MODELER; 18 | } 19 | } -------------------------------------------------------------------------------- /plot-diagnostic-file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DATA_FILE=/tmp/data.csv 4 | PLOT_FILE=/tmp/gnuplot.plot 5 | 6 | cat << EOPLOT > $PLOT_FILE 7 | set datafile separator "," 8 | set xdata time 9 | set timefmt "%s" 10 | set format x "%m/%d/%Y %H:%M:%S" 11 | set term dumb 12 | 13 | set key autotitle columnhead 14 | 15 | plot "$DATA_FILE" using 2:1 with linespoints 16 | EOPLOT 17 | 18 | rm -f $DATA_FILE 19 | touch $DATA_FILE 20 | echo '"START"','"PHASE"','"DURATION"' > $DATA_FILE 21 | 22 | jq -r '.phases[]| { start: .start, phase: ("\"" + .phase + "\""), duration: (.end - .start) }| join(",")' < "$1" >> $DATA_FILE 23 | 24 | gnuplot $PLOT_FILE 25 | 26 | 27 | -------------------------------------------------------------------------------- /runtime/vm/src/concurrency/spin_lock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace vm::concurrency { 8 | typedef std::shared_ptr spin_lock_latch; 9 | 10 | struct spin_lock_ref { 11 | explicit spin_lock_ref(spin_lock_latch ref); 12 | ~spin_lock_ref(); 13 | 14 | void unlock(); 15 | private: 16 | spin_lock_latch ref; 17 | }; 18 | 19 | class spin_lock { 20 | public: 21 | spin_lock(); 22 | ~spin_lock(); 23 | 24 | spin_lock_ref lock(); 25 | private: 26 | spin_lock_latch latch; 27 | }; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /passes/src/mutations/pass_value_class_ir_transformer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type_registry.h" 4 | #include "../pass.h" 5 | 6 | namespace scc::passes::mutations { 7 | using namespace scc::type_system; 8 | 9 | class pass_value_class_ir_transformer : public pass { 10 | public: 11 | explicit pass_value_class_ir_transformer(const std::shared_ptr &types); 12 | ~pass_value_class_ir_transformer() override; 13 | 14 | void execute(scc::ast::ast_root &root) const override; 15 | diagnostic::diagnostic_phase_id pass_phase() const override; 16 | private: 17 | const std::shared_ptr types; 18 | }; 19 | } -------------------------------------------------------------------------------- /ast/src/nmethod_call.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "metadata.h" 8 | #include "nfunction_call.h" 9 | #include "node.h" 10 | 11 | namespace scc::ast { 12 | using std::list; 13 | using std::variant; 14 | using std::shared_ptr; 15 | 16 | using namespace scc::common; 17 | 18 | struct nmethod_call : public expression { 19 | nmethod_call(); 20 | ~nmethod_call(); 21 | 22 | expression_ref left; 23 | std::string method; 24 | list> arguments; 25 | 26 | void to_json(json &j) override; 27 | }; 28 | } -------------------------------------------------------------------------------- /ast/src/nconstant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "metadata.h" 7 | #include "node.h" 8 | 9 | namespace scc::ast { 10 | using std::variant; 11 | using namespace scc::common; 12 | 13 | enum class nconstant_type : unsigned char { 14 | INTEGER, FLOATING, BOOLEAN, STRING 15 | }; 16 | 17 | typedef variant nconstant_info; 18 | 19 | struct nconstant : public expression { 20 | nconstant(); 21 | ~nconstant(); 22 | 23 | nconstant_type type; 24 | nconstant_info content; 25 | 26 | void to_json(json& j) override; 27 | }; 28 | } -------------------------------------------------------------------------------- /lexer/src/token.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "token_type.h" 8 | #include "metadata.h" 9 | 10 | namespace scc::lexer { 11 | using std::string; 12 | using std::unique_ptr; 13 | using std::variant; 14 | using namespace scc::common; 15 | 16 | typedef variant token_metadata; 17 | 18 | struct token_source { 19 | string source; 20 | size_t line, column; 21 | }; 22 | 23 | struct token { 24 | token_type type; 25 | token_source source; 26 | token_metadata metadata; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /passes/src/mutations/pass_entity_class_method_resolution.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type_registry.h" 4 | #include "../pass.h" 5 | 6 | namespace scc::passes::mutations { 7 | using namespace scc::type_system; 8 | 9 | class pass_entity_class_method_resolution : public pass { 10 | public: 11 | explicit pass_entity_class_method_resolution(const std::shared_ptr &types); 12 | ~pass_entity_class_method_resolution() override; 13 | 14 | void execute(scc::ast::ast_root &root) const override; 15 | diagnostic::diagnostic_phase_id pass_phase() const override; 16 | private: 17 | const std::shared_ptr types; 18 | }; 19 | } -------------------------------------------------------------------------------- /type-system/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(type-system) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include_directories(../common/src) 7 | include_directories(../diagnostic/src) 8 | 9 | add_library(type-system src/type.h src/type_registry.cpp src/type_registry.h src/memory/internal_modeler.cpp src/memory/internal_modeler.h) 10 | 11 | enable_testing() 12 | find_package(GTest REQUIRED) 13 | include(GoogleTest) 14 | 15 | add_executable(type-system_tests src/type-system.spec.cpp src/type_registry.test.cpp src/memory/internal_modeler.test.cpp) 16 | target_link_libraries(type-system_tests type-system GTest::GTest GTest::Main diagnostic) 17 | gtest_discover_tests(type-system_tests) -------------------------------------------------------------------------------- /passes/src/mutations/pass_value_class_ir_transformer.cpp: -------------------------------------------------------------------------------- 1 | #include "pass_value_class_ir_transformer.h" 2 | 3 | namespace scc::passes::mutations { 4 | 5 | pass_value_class_ir_transformer::pass_value_class_ir_transformer(const std::shared_ptr &types) 6 | : types(types) { 7 | 8 | } 9 | 10 | pass_value_class_ir_transformer::~pass_value_class_ir_transformer() = default; 11 | 12 | void pass_value_class_ir_transformer::execute(ast::ast_root &root) const { 13 | 14 | } 15 | 16 | diagnostic::diagnostic_phase_id pass_value_class_ir_transformer::pass_phase() const { 17 | return diagnostic::diagnostic_phase_id::PASS_VALUE_CLASS_IR_TRANSFORMER; 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /passes/src/analyzers/pass_internal_modeler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../pass.h" 4 | #include "type_registry.h" 5 | #include "memory/internal_modeler.h" 6 | 7 | namespace scc::passes::analyzers { 8 | using namespace scc::type_system; 9 | 10 | class pass_internal_modeler : public pass { 11 | public: 12 | explicit pass_internal_modeler(const std::shared_ptr &modeler); 13 | ~pass_internal_modeler() override; 14 | 15 | void execute(scc::ast::ast_root &root) const override; 16 | diagnostic::diagnostic_phase_id pass_phase() const override; 17 | 18 | private: 19 | const std::shared_ptr modeler; 20 | }; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor_system.cpp: -------------------------------------------------------------------------------- 1 | #include "actor_system.h" 2 | 3 | namespace vm::actor { 4 | actor_system::actor_system() { 5 | 6 | } 7 | 8 | actor_system::~actor_system() { 9 | 10 | } 11 | 12 | std::shared_ptr actor_system::resolve_by_address(const address &addr) { 13 | auto locked = lock.lock(); 14 | auto it = registry.find(addr); 15 | 16 | if (it == registry.end()) { 17 | return {}; 18 | } 19 | 20 | return it->second; 21 | } 22 | 23 | void actor_system::register_actor(const std::shared_ptr &actor_to_register) { 24 | auto locked = lock.lock(); 25 | registry[actor_to_register->self_address] = actor_to_register; 26 | } 27 | } -------------------------------------------------------------------------------- /ast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(ast) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include_directories(../diagnostic/src) 7 | include_directories(../common/src) 8 | 9 | add_library(ast 10 | src/node.cpp src/node.h 11 | src/nclass.cpp src/nclass.h 12 | src/nconstant.cpp src/nconstant.h 13 | src/nfunction_call.cpp src/nfunction_call.h 14 | src/nidentifier.cpp src/nidentifier.h 15 | src/nlet.cpp src/nlet.h 16 | src/nmethod_call.cpp src/nmethod_call.h 17 | src/nspawn_entity.cpp src/nspawn_entity.h 18 | src/ntype.cpp src/ntype.h 19 | src/ir/nstruct.cpp src/ir/nstruct.h 20 | src/ast.h 21 | ) 22 | 23 | target_link_libraries(ast common) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sonata 2 | [![.github/workflows/health.yml](https://github.com/kmruiz/sonata/actions/workflows/health.yml/badge.svg)](https://github.com/kmruiz/sonata/actions/workflows/health.yml) ![License](https://img.shields.io/badge/License-MIT-brightgreen) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/kmruiz/sonata) 3 | 4 | This repository contains the source code of the Sonata compiler and it's runtime. 5 | 6 | ## What is Sonata? 7 | 8 | Sonata is an opinionated, distributed-first programming language based in the [actor model](https://en.wikipedia.org/wiki/Actor_model). 9 | Sonata is not designed to develop system tools, but it is designed as a high-level programming language to implement 10 | highly distributed software. 11 | 12 | -------------------------------------------------------------------------------- /ast/src/nspawn_entity.cpp: -------------------------------------------------------------------------------- 1 | #include "nconstant.h" 2 | #include "nspawn_entity.h" 3 | 4 | namespace scc::ast { 5 | nspawn_entity::nspawn_entity() = default; 6 | nspawn_entity::~nspawn_entity() = default; 7 | 8 | void nspawn_entity::to_json(json &j) { 9 | j["entity"] = entity_name; 10 | std::vector args; 11 | for (const auto &arg : arguments) { 12 | json x; 13 | if (holds_alternative(arg)) { 14 | get(arg)->to_json(x); 15 | } else { 16 | get(arg)->to_json(x); 17 | } 18 | 19 | args.push_back(x); 20 | } 21 | 22 | j["arguments"] = args; 23 | } 24 | } -------------------------------------------------------------------------------- /ast/src/nmethod_call.cpp: -------------------------------------------------------------------------------- 1 | #include "nconstant.h" 2 | #include "nmethod_call.h" 3 | 4 | namespace scc::ast { 5 | nmethod_call::nmethod_call() = default; 6 | nmethod_call::~nmethod_call() = default; 7 | 8 | void nmethod_call::to_json(json &j) { 9 | left->to_json(j["left"]); 10 | j["method"] = method; 11 | std::vector args; 12 | for (const auto &arg : arguments) { 13 | json x; 14 | if (holds_alternative(arg)) { 15 | get(arg)->to_json(x); 16 | } else { 17 | get(arg)->to_json(x); 18 | } 19 | 20 | args.push_back(x); 21 | } 22 | 23 | j["arguments"] = args; 24 | } 25 | } -------------------------------------------------------------------------------- /ast/src/ir/nstruct.cpp: -------------------------------------------------------------------------------- 1 | #include "nstruct.h" 2 | 3 | namespace scc::ast::ir { 4 | void nstruct::to_json(json &j) { 5 | 6 | } 7 | 8 | void nstruct_malloc::to_json(json &j) { 9 | 10 | } 11 | 12 | void nstruct_free::to_json(json &j) { 13 | 14 | } 15 | 16 | void nstruct_register_to_mailbox::to_json(json &j) { 17 | 18 | } 19 | 20 | void nstruct_function_def::to_json(json &j) { 21 | 22 | } 23 | 24 | void nstruct_function_call::to_json(json &j) { 25 | 26 | } 27 | 28 | void nstruct_bitbag_set::to_json(json &j) { 29 | 30 | } 31 | 32 | void nstruct_direct_set::to_json(json &j) { 33 | 34 | } 35 | 36 | void nstruct_bitbag_get::to_json(json &j) { 37 | 38 | } 39 | 40 | void nstruct_direct_get::to_json(json &j) { 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(scc) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_subdirectory(ast) 7 | add_subdirectory(backend-llvm) 8 | add_subdirectory(common) 9 | add_subdirectory(diagnostic) 10 | add_subdirectory(discovery) 11 | add_subdirectory(lexer) 12 | add_subdirectory(parser) 13 | add_subdirectory(passes) 14 | add_subdirectory(runtime) 15 | add_subdirectory(type-system) 16 | 17 | include_directories(ast/src) 18 | include_directories(backend-llvm/src) 19 | include_directories(common/src) 20 | include_directories(diagnostic/src) 21 | include_directories(discovery/src) 22 | include_directories(lexer/src) 23 | include_directories(type-system/src) 24 | 25 | add_executable(scc main.cpp) 26 | target_link_libraries(scc lexer diagnostic ast parser discovery common passes backend-llvm) 27 | -------------------------------------------------------------------------------- /passes/src/mutations/pass_entity_class_ir_transformer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type_registry.h" 4 | #include "../pass.h" 5 | 6 | namespace scc::passes::mutations { 7 | using namespace scc::type_system; 8 | 9 | class pass_entity_class_ir_transformer : public pass { 10 | public: 11 | explicit pass_entity_class_ir_transformer(const std::shared_ptr &types); 12 | ~pass_entity_class_ir_transformer() override; 13 | 14 | void execute(scc::ast::ast_root &root) const override; 15 | diagnostic::diagnostic_phase_id pass_phase() const override; 16 | private: 17 | scc::ast::expression_ref parse_self_refs(std::shared_ptr &type, scc::ast::expression_ref &expr, scc::ast::ast_block &block) const; 18 | 19 | const std::shared_ptr types; 20 | }; 21 | } -------------------------------------------------------------------------------- /runtime/vm/src/mailbox/fiber.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../actor/actor.h" 9 | 10 | namespace vm::mailbox { 11 | using vm::actor::actor; 12 | using vm::actor::base_actor_state; 13 | using vm::actor::address; 14 | 15 | typedef std::function(address)> actor_resolver; 16 | 17 | class fiber { 18 | public: 19 | fiber( 20 | std::shared_ptr mailbox, 21 | actor_resolver resolver 22 | ); 23 | ~fiber(); 24 | 25 | void run(); 26 | void stop(); 27 | private: 28 | std::atomic_bool running; 29 | std::thread running_thread; 30 | std::shared_ptr bound_mailbox; 31 | actor_resolver current_resolver; 32 | }; 33 | } -------------------------------------------------------------------------------- /ast/src/nconstant.cpp: -------------------------------------------------------------------------------- 1 | #include "nconstant.h" 2 | 3 | namespace scc::ast { 4 | nconstant::nconstant() = default; 5 | nconstant::~nconstant() = default; 6 | 7 | void nconstant::to_json(json &j) { 8 | j["id"] = this->id; 9 | j["node"] = "constant"; 10 | 11 | if (holds_alternative(this->content)) { 12 | j["value"] = get(this->content); 13 | } 14 | 15 | if (holds_alternative(this->content)) { 16 | j["value"] = get(this->content); 17 | } 18 | 19 | if (holds_alternative(this->content)) { 20 | j["value"] = get(this->content); 21 | } 22 | 23 | if (holds_alternative(this->content)) { 24 | j["value"] = get(this->content); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /runtime/vm/src/concurrency/spin_lock.cpp: -------------------------------------------------------------------------------- 1 | #include "spin_lock.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace vm::concurrency { 7 | spin_lock_ref::spin_lock_ref(spin_lock_latch ref) : ref(std::move(ref)) { 8 | } 9 | 10 | spin_lock_ref::~spin_lock_ref() { 11 | this->ref->store(false); 12 | } 13 | 14 | void spin_lock_ref::unlock() { 15 | this->ref->store(false); 16 | } 17 | 18 | spin_lock::spin_lock() { 19 | this->latch = std::make_shared(false); 20 | } 21 | 22 | spin_lock::~spin_lock() = default; 23 | 24 | spin_lock_ref spin_lock::lock() { 25 | bool unlatched = false; 26 | while (!latch->compare_exchange_strong(unlatched, true, std::memory_order_acquire)) { 27 | unlatched = false; 28 | } 29 | 30 | return spin_lock_ref(this->latch); 31 | } 32 | } -------------------------------------------------------------------------------- /compile-and-run-playground.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | PATH_CLANG_LIB_BEGIN=/usr/lib/clang/13/lib/linux/clang_rt.crtbegin-x86_64.o 6 | PATH_CLANG_LIB_END=/usr/lib/clang/13/lib/linux/clang_rt.crtend-x86_64.o 7 | 8 | echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid > /dev/null 9 | cmake --build cmake-build-debug --target scc -j 12 10 | cmake --build cmake-build-debug --target sonata-rt -j 12 11 | ./cmake-build-debug/scc -D./cmake-build-debug/debug-diagnostic.json -LDEBUG sn_examples/playground 12 | clang -c sn_examples/playground/extern/printbb.c -o lib.o 13 | clang++ -no-pie output.o lib.o ./cmake-build-debug/runtime/vm/libsonata-rt.a 14 | echo '---------------------------------------------------------------------------------------' 15 | perf record ./a.out 16 | echo 17 | echo '---------------------------------------------------------------------------------------' 18 | rm a.out 19 | rm output.o 20 | rm lib.o -------------------------------------------------------------------------------- /ast/src/nfunction_call.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "metadata.h" 8 | #include "node.h" 9 | 10 | namespace scc::ast { 11 | using std::list; 12 | using std::variant; 13 | using std::shared_ptr; 14 | 15 | using namespace scc::common; 16 | 17 | struct nfunction_call_named_argument : public expression { 18 | string name; 19 | expression_ref expression; 20 | 21 | void to_json(json &j) override; 22 | }; 23 | 24 | typedef shared_ptr nfunction_call_named_argument_ref; 25 | 26 | struct nfunction_call : public expression { 27 | nfunction_call(); 28 | ~nfunction_call(); 29 | 30 | expression_ref left; 31 | list> arguments; 32 | 33 | void to_json(json &j) override; 34 | }; 35 | } -------------------------------------------------------------------------------- /parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(parser) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include_directories(../diagnostic/src) 7 | include_directories(../ast/src) 8 | include_directories(../common/src) 9 | include_directories(../lexer/src) 10 | 11 | add_library(parser src/parser.cpp src/parser.h) 12 | target_link_libraries(parser lexer ast) 13 | 14 | enable_testing() 15 | find_package(GTest REQUIRED) 16 | include(GoogleTest) 17 | 18 | add_executable(parser_tests 19 | src/parser.spec.cpp 20 | src/parser_class.test.cpp 21 | src/parser_function_call.test.cpp 22 | src/parser_method_call.test.cpp 23 | src/parser_spawn.test.cpp 24 | src/parser_let.test.cpp 25 | src/parser_type.test.cpp 26 | src/test-includes/includes.hpp 27 | ) 28 | target_link_libraries(parser_tests lexer GTest::GTest GTest::Main diagnostic parser) 29 | gtest_discover_tests(parser_tests) -------------------------------------------------------------------------------- /ast/src/nfunction_call.cpp: -------------------------------------------------------------------------------- 1 | #include "nconstant.h" 2 | #include "nfunction_call.h" 3 | 4 | 5 | namespace scc::ast { 6 | nfunction_call::nfunction_call() = default; 7 | nfunction_call::~nfunction_call() = default; 8 | 9 | void nfunction_call_named_argument::to_json(json &j) { 10 | j["name"] = name; 11 | expression->to_json(j["expression"]); 12 | } 13 | 14 | void nfunction_call::to_json(json &j) { 15 | left->to_json(j["left"]); 16 | std::vector args; 17 | for (const auto &arg : arguments) { 18 | json x; 19 | if (holds_alternative(arg)) { 20 | get(arg)->to_json(x); 21 | } else { 22 | get(arg)->to_json(x); 23 | } 24 | 25 | args.push_back(x); 26 | } 27 | 28 | j["arguments"] = args; 29 | } 30 | } -------------------------------------------------------------------------------- /lexer/src/token_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace scc::lexer { 4 | enum class token_type : unsigned char { 5 | COMMENT, 6 | WHITESPACE, 7 | NEW_LINE, 8 | END_OF_FILE, 9 | 10 | IDENTIFIER, 11 | INTEGER, 12 | FLOATING, 13 | 14 | COLON, 15 | COMMA, 16 | DOT, 17 | 18 | OPEN_PAREN, 19 | OPEN_BRACKET, 20 | OPEN_BRACE, 21 | CLOSE_PAREN, 22 | CLOSE_BRACKET, 23 | CLOSE_BRACE, 24 | STRING, 25 | 26 | AT, 27 | HASH, 28 | TILDE, 29 | QUESTION_MARK, 30 | EXCLAMATION_MARK, 31 | EQUALS, 32 | LESS_THAN, 33 | GREATER_THAN, 34 | PLUS, 35 | MINUS, 36 | MULTIPLY, 37 | DIVIDE, 38 | CARET, 39 | PERCENT, 40 | AMPERSAND, 41 | 42 | UNKNOWN 43 | }; 44 | 45 | std::string to_string(const token_type &type); 46 | } -------------------------------------------------------------------------------- /ast/src/nlet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node.h" 4 | 5 | namespace scc::ast { 6 | struct nlet : public node { 7 | nlet(); 8 | ~nlet(); 9 | 10 | bool mutable_p; 11 | string name; 12 | type_constraints constraints; 13 | optional expression; 14 | 15 | void to_json(json& j) override; 16 | }; 17 | 18 | struct nlet_function_named_parameter { 19 | string name; 20 | type_constraints type; 21 | }; 22 | 23 | typedef std::shared_ptr nlet_function_named_parameter_ref; 24 | typedef variant nlet_function_parameter; 25 | 26 | struct nlet_function : public node { 27 | nlet_function(); 28 | ~nlet_function(); 29 | 30 | string name; 31 | list parameters; 32 | type_constraints return_type; 33 | optional body; 34 | bool external; 35 | 36 | void to_json(json& j) override; 37 | }; 38 | } -------------------------------------------------------------------------------- /runtime/vm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(runtime_vm) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_library(sonata-rt 7 | src/mailbox/mailbox.cpp 8 | src/mailbox/mailbox.h 9 | src/core/address.cpp 10 | src/core/address.h 11 | src/concurrency/spin_lock.cpp 12 | src/concurrency/spin_lock.h 13 | src/actor/actor.cpp 14 | src/actor/actor.h 15 | src/actor/actor_type.cpp 16 | src/actor/actor_type.h 17 | src/actor/actor_system.cpp 18 | src/actor/actor_system.h 19 | src/mailbox/fiber.cpp 20 | src/mailbox/fiber.h 21 | src/api.cpp 22 | src/api.h) 23 | 24 | target_compile_options(sonata-rt PRIVATE -fpie) 25 | 26 | enable_testing() 27 | find_package(GTest REQUIRED) 28 | include(GoogleTest) 29 | 30 | add_executable(sonata-rt_tests src/core/address.test.cpp src/mailbox/mailbox.test.cpp src/mailbox/fiber.test.cpp src/actor/actor.test.cpp) 31 | target_link_libraries(sonata-rt_tests sonata-rt GTest::GTest GTest::Main) 32 | gtest_discover_tests(sonata-rt_tests) -------------------------------------------------------------------------------- /common/src/metadata.cpp: -------------------------------------------------------------------------------- 1 | #include "metadata.h" 2 | 3 | namespace scc::common { 4 | 5 | void to_json(json &j, const info_comment &comment) { 6 | j["type"] = "comment"; 7 | j["content"] = comment.content; 8 | } 9 | 10 | void to_json(json &j, const info_identifier &identifier) { 11 | j["type"] = "identifier"; 12 | j["content"] = identifier.content; 13 | } 14 | 15 | void to_json(json &j, const info_integer &integer) { 16 | j["type"] = "integer"; 17 | j["representation"] = integer.representation; 18 | j["base"] = integer.base; 19 | } 20 | 21 | void to_json(json &j, const info_floating &floating) { 22 | j["type"] = "floating"; 23 | j["representation"] = floating.representation; 24 | j["base"] = floating.base; 25 | } 26 | 27 | void to_json(json &j, const info_boolean &boolean) { 28 | j["type"] = "boolean"; 29 | j["value"] = boolean.value; 30 | } 31 | 32 | void to_json(json &j, const info_string &string) { 33 | j["type"] = "string"; 34 | j["value"] = string.content; 35 | } 36 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kevin Mas Ruiz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /type-system/src/memory/internal_modeler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../type_registry.h" 4 | #include "../type.h" 5 | 6 | namespace scc::type_system::memory { 7 | class internal_modeler { 8 | public: 9 | explicit internal_modeler(const unsigned int goal_cache_size, std::shared_ptr &types); 10 | ~internal_modeler(); 11 | 12 | void model_all_types() const; 13 | void model_type(std::shared_ptr &type) const; 14 | private: 15 | const unsigned int goal_cache_size; 16 | std::shared_ptr types; 17 | 18 | std::shared_ptr boolean_type; 19 | std::shared_ptr byte_type; 20 | std::shared_ptr short_type; 21 | std::shared_ptr integer_type; 22 | std::shared_ptr long_type; 23 | std::shared_ptr floating_type; 24 | std::shared_ptr double_type; 25 | 26 | void merge_into_parent(std::shared_ptr &root, unsigned int &offset, bit_bag ¤t_bitbag, unsigned int &remaining_from_bitbag, const std::shared_ptr &field) const; 27 | void pad_to_cacheable(std::shared_ptr &root) const; 28 | }; 29 | } -------------------------------------------------------------------------------- /common/src/metadata.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace scc::common { 7 | using std::string; 8 | using json = nlohmann::json; 9 | 10 | enum class numeric_base : unsigned char { 11 | BINARY = 2, 12 | OCTAL = 8, 13 | DECIMAL = 10, 14 | HEXADECIMAL = 16 15 | }; 16 | 17 | struct info_comment { 18 | string content; 19 | }; 20 | 21 | struct info_identifier { 22 | string content; 23 | }; 24 | 25 | struct info_integer { 26 | string representation; 27 | numeric_base base; 28 | }; 29 | 30 | struct info_floating { 31 | string representation; 32 | numeric_base base; 33 | }; 34 | 35 | struct info_boolean { 36 | bool value; 37 | }; 38 | 39 | struct info_string { 40 | string content; 41 | }; 42 | 43 | void to_json(json &j, const info_comment &comment); 44 | void to_json(json &j, const info_identifier &identifier); 45 | void to_json(json &j, const info_integer &integer); 46 | void to_json(json &j, const info_floating &floating); 47 | void to_json(json &j, const info_boolean &boolean); 48 | void to_json(json &j, const info_string &string); 49 | } 50 | -------------------------------------------------------------------------------- /type-system/src/type_registry.cpp: -------------------------------------------------------------------------------- 1 | #include "type_registry.h" 2 | 3 | #include 4 | 5 | typedef std::map>::value_type map_registry_entry; 6 | 7 | namespace scc::type_system { 8 | type_registry::type_registry() = default; 9 | type_registry::~type_registry() = default; 10 | 11 | std::shared_ptr type_registry::resolve(const std::string &name) { 12 | if (map.contains(name)) { 13 | return map[name]; 14 | } 15 | 16 | auto res = std::make_shared(); 17 | map[name] = res; 18 | res->name = name; 19 | res->kind = scc::type_system::type_kind::UNKNOWN; 20 | 21 | return res; 22 | } 23 | 24 | bool type_registry::defined(const std::string &name) { 25 | if (map.contains(name)) { 26 | return map[name]->kind != scc::type_system::type_kind::UNKNOWN; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | std::list> type_registry::all_types() { 33 | std::list> return_value; 34 | std::transform(map.begin(), map.end(), std::back_inserter(return_value), [](const map_registry_entry &entry) { 35 | return entry.second; 36 | }); 37 | return return_value; 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /backend-llvm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(backend-llvm) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | execute_process(COMMAND llvm-config --libs OUTPUT_VARIABLE LIBS) 7 | execute_process(COMMAND llvm-config --system-libs OUTPUT_VARIABLE SYS_LIBS) 8 | execute_process(COMMAND llvm-config --ldflags OUTPUT_VARIABLE LDF) 9 | execute_process(COMMAND llvm-config --includedir OUTPUT_VARIABLE INCLUDE_DIR) 10 | execute_process(COMMAND llvm-config --cxxflags OUTPUT_VARIABLE CMAKE_CXX_FLAGS) 11 | 12 | string(STRIP ${LIBS} LIBS) 13 | string(STRIP ${SYS_LIBS} SYS_LIBS) 14 | string(STRIP ${LDF} LDF) 15 | string(STRIP ${INCLUDE_DIR} INCLUDE_DIR) 16 | string(STRIP ${CMAKE_CXX_FLAGS} CMAKE_CXX_FLAGS) 17 | 18 | include_directories(../ast/src) 19 | include_directories(../diagnostic/src) 20 | include_directories(../common/src) 21 | include_directories(../type-system/src) 22 | 23 | add_definitions(-fexceptions) 24 | add_library(backend-llvm 25 | src/backend.cpp 26 | src/backend.h 27 | src/ir_builder.cpp 28 | src/ir_builder.h 29 | src/intrinsics/intrinsics.h 30 | src/runtime/runtime.h) 31 | 32 | target_link_libraries(backend-llvm ${LIBS} ${SYS_LIBS} ${LDF} diagnostic ast type-system) 33 | 34 | target_include_directories(backend-llvm PUBLIC ${INCLUDE_DIR} /usr/lib/llvm-13/include/) 35 | -------------------------------------------------------------------------------- /runtime/vm/src/mailbox/fiber.cpp: -------------------------------------------------------------------------------- 1 | #include "fiber.h" 2 | 3 | #include 4 | 5 | namespace vm::mailbox { 6 | fiber::fiber( 7 | std::shared_ptr mailbox, 8 | actor_resolver resolver 9 | ) : 10 | bound_mailbox(std::move(mailbox)), 11 | running(true), 12 | current_resolver(resolver), 13 | running_thread(std::thread([this]() { 14 | this->run(); 15 | })) 16 | { 17 | } 18 | 19 | fiber::~fiber() { 20 | this->stop(); 21 | } 22 | 23 | void fiber::run() { 24 | bool stopped = false; 25 | while (!running.compare_exchange_strong(stopped, false, std::memory_order_acquire)) { 26 | stopped = false; 27 | 28 | auto msg = bound_mailbox->dequeue(); 29 | if (msg) { 30 | auto receiver = current_resolver(msg->receiver); 31 | 32 | if (receiver) { 33 | receiver->process_message(std::move(msg)); 34 | } 35 | } 36 | 37 | std::this_thread::sleep_for(std::chrono::microseconds(50)); 38 | } 39 | } 40 | 41 | void fiber::stop() { 42 | running.exchange(false, std::memory_order_acquire); 43 | if (this->running_thread.joinable()) { 44 | this->running_thread.join(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /runtime/vm/src/mailbox/mailbox.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by kevin on 4/27/23. 3 | // 4 | 5 | #include "mailbox.h" 6 | 7 | using namespace vm::mailbox; 8 | 9 | mailbox::mailbox() { 10 | this->released = false; 11 | } 12 | 13 | mailbox::~mailbox() { 14 | 15 | } 16 | 17 | void mailbox::enqueue(std::unique_ptr message) { 18 | if (released) { 19 | return; 20 | } 21 | 22 | auto ref = lock.lock(); 23 | queue.push_back(std::move(message)); 24 | } 25 | 26 | std::unique_ptr mailbox::dequeue() { 27 | auto ref = lock.lock(); 28 | if (!replies.empty()) { 29 | auto reply = std::move(replies.front()); 30 | replies.pop_front(); 31 | 32 | return reply; 33 | } 34 | 35 | if (queue.empty()) { 36 | return {nullptr}; 37 | } 38 | 39 | auto value = std::move(queue.front()); 40 | queue.pop_front(); 41 | return value; 42 | } 43 | 44 | void mailbox::wait_for_reply(std::unique_ptr message) { 45 | auto ref = lock.lock(); 46 | replies.push_back(std::move(message)); 47 | } 48 | 49 | void mailbox::reply(correlation_id id, const std::string &answer) { 50 | auto ref = lock.lock(); 51 | for (auto &i : replies) { 52 | if (i->reply_on == id) { 53 | i->reply = answer; 54 | return; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /passes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(parser) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include_directories(../diagnostic/src) 7 | include_directories(../ast/src) 8 | include_directories(../common/src) 9 | include_directories(../type-system/src) 10 | 11 | add_library(passes 12 | src/pass.h 13 | src/analyzers/pass_detect_classes.cpp src/analyzers/pass_detect_classes.h 14 | src/analyzers/pass_internal_modeler.cpp src/analyzers/pass_internal_modeler.h 15 | src/mutations/pass_value_class_ir_transformer.cpp src/mutations/pass_value_class_ir_transformer.h 16 | src/mutations/pass_entity_class_ir_transformer.cpp src/mutations/pass_entity_class_ir_transformer.h 17 | src/mutations/pass_entity_class_method_resolution.cpp src/mutations/pass_entity_class_method_resolution.h 18 | src/pass_manager.cpp src/pass_manager.h 19 | ) 20 | 21 | target_link_libraries(passes ast type-system) 22 | 23 | # 24 | #enable_testing() 25 | #find_package(GTest REQUIRED) 26 | #include(GoogleTest) 27 | # 28 | #add_executable(passes_test 29 | # src/parser.spec.cpp 30 | # src/parser_class.test.cpp 31 | # src/parser_function_call.test.cpp 32 | # src/parser_let.test.cpp 33 | # src/test-includes/includes.hpp 34 | #) 35 | #target_link_libraries(passes_test lexer GTest::GTest GTest::Main diagnostic passes) 36 | #gtest_discover_tests(passes_test) -------------------------------------------------------------------------------- /runtime/vm/src/mailbox/mailbox.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "mailbox.h" 4 | 5 | TEST(mailbox, pushes_and_pulls_message_once) { 6 | auto mb = vm::mailbox::mailbox(); 7 | auto enqueued_message = std::make_unique(); 8 | enqueued_message->message = "example"; 9 | 10 | mb.enqueue(std::move(enqueued_message)); 11 | auto dequeued_msg = mb.dequeue(); 12 | 13 | ASSERT_EQ(dequeued_msg->message, "example"); 14 | ASSERT_FALSE(mb.dequeue()); 15 | } 16 | 17 | TEST(mailbox, acts_as_fifo) { 18 | auto mb = vm::mailbox::mailbox(); 19 | auto emsg1 = std::make_unique(); 20 | auto emsg2 = std::make_unique(); 21 | emsg1->message = "1"; 22 | emsg2->message = "2"; 23 | 24 | mb.enqueue(std::move(emsg1)); 25 | mb.enqueue(std::move(emsg2)); 26 | 27 | auto dmsg1 = mb.dequeue(); 28 | auto dmsg2 = mb.dequeue(); 29 | 30 | ASSERT_EQ(dmsg1->message, "1"); 31 | ASSERT_EQ(dmsg2->message, "2"); 32 | ASSERT_FALSE(mb.dequeue()); 33 | } 34 | 35 | TEST(mailbox, returns_a_reply_if_any) { 36 | auto mb = vm::mailbox::mailbox(); 37 | auto emsg1 = std::make_unique(); 38 | 39 | emsg1->message = "1"; 40 | emsg1->reply_on = 0; 41 | 42 | mb.wait_for_reply(std::move(emsg1)); 43 | mb.reply(0, "hey"); 44 | 45 | auto dmsg = mb.dequeue(); 46 | ASSERT_EQ(dmsg->reply, "hey"); 47 | } -------------------------------------------------------------------------------- /passes/src/pass_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "pass_manager.h" 2 | 3 | #include "analyzers/pass_detect_classes.h" 4 | #include "analyzers/pass_internal_modeler.h" 5 | #include "mutations/pass_value_class_ir_transformer.h" 6 | #include "mutations/pass_entity_class_ir_transformer.h" 7 | #include "mutations/pass_entity_class_method_resolution.h" 8 | 9 | namespace scc::passes { 10 | 11 | pass_manager::pass_manager(std::shared_ptr &types) { 12 | validations.emplace_back(std::make_unique(types)); 13 | validations.emplace_back(std::make_unique(std::make_shared(64, types))); 14 | 15 | mutations.emplace_back(std::make_unique(types)); 16 | mutations.emplace_back(std::make_unique(types)); 17 | mutations.emplace_back(std::make_unique(types)); 18 | } 19 | 20 | void pass_manager::run(ast::ast_root &root) { 21 | for (auto &v : validations) { // these can run in parallel at some point 22 | D_START_PHASE(v->pass_phase()); 23 | v->execute(root); 24 | D_END_PHASE(); 25 | } 26 | 27 | for (auto &v : mutations) { 28 | D_START_PHASE(v->pass_phase()); 29 | v->execute(root); 30 | D_END_PHASE(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /runtime/vm/src/mailbox/mailbox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../core/address.h" 8 | #include "../concurrency/spin_lock.h" 9 | 10 | namespace vm::mailbox { 11 | struct message_trace { 12 | core::address stepped_actor; 13 | std::string message; 14 | }; 15 | 16 | typedef uint64_t correlation_id; 17 | 18 | struct message { 19 | core::address sender; 20 | core::address receiver; 21 | std::string message; 22 | std::map metadata; 23 | std::list arguments; 24 | std::list traces; 25 | correlation_id reply_on; 26 | std::string reply; 27 | 28 | inline bool has_reply() { 29 | return !reply.empty(); 30 | } 31 | 32 | inline bool is_a_reply() { 33 | return reply_on != 0; 34 | } 35 | }; 36 | 37 | class mailbox { 38 | public: 39 | mailbox(); 40 | ~mailbox(); 41 | 42 | void enqueue(std::unique_ptr message); 43 | std::unique_ptr dequeue(); 44 | void wait_for_reply(std::unique_ptr message); 45 | void reply(correlation_id id, const std::string &answer); 46 | private: 47 | concurrency::spin_lock lock; 48 | std::list> queue; 49 | std::list> replies; 50 | bool released; 51 | }; 52 | 53 | } -------------------------------------------------------------------------------- /ast/src/nclass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "metadata.h" 7 | #include "node.h" 8 | 9 | namespace scc::ast { 10 | using std::variant; 11 | using std::shared_ptr; 12 | using namespace scc::common; 13 | 14 | enum class nclass_type : unsigned char { 15 | VALUE, ENTITY, ENTITY_INTERFACE, CAPABILITY 16 | }; 17 | 18 | enum class nclass_visibility : unsigned char { 19 | PUBLIC, PRIVATE 20 | }; 21 | 22 | struct nclass_primary_field { 23 | type_constraints type; 24 | string name; 25 | optional initial_value; 26 | }; 27 | 28 | struct nclass_capability_requirement { 29 | string field; 30 | string capability; 31 | }; 32 | 33 | struct nclass : public node { 34 | nclass_type type; 35 | nclass_visibility visibility; 36 | string package; 37 | string name; 38 | list> fields; 39 | list> requirements; 40 | optional body; 41 | 42 | void to_json(json& j) override; 43 | }; 44 | 45 | struct nclass_self_set : public expression { 46 | list selector; 47 | expression_ref value; 48 | 49 | void to_json(json& j) override; 50 | }; 51 | 52 | struct nclass_self_get : public expression { 53 | list selector; 54 | 55 | void to_json(json& j) override; 56 | }; 57 | } -------------------------------------------------------------------------------- /type-system/src/type_registry.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "type_registry.h" 5 | 6 | TEST(type_registry, types_are_not_registered_by_default) { 7 | scc::type_system::type_registry reg; 8 | ASSERT_FALSE(reg.defined("type")); 9 | } 10 | 11 | TEST(type_registry, types_are_not_registered_when_resolved_first_time) { 12 | scc::type_system::type_registry reg; 13 | reg.resolve("type"); 14 | ASSERT_FALSE(reg.defined("type")); 15 | } 16 | 17 | 18 | TEST(type_registry, when_a_type_is_resolved_and_does_not_exist_return_fillable_placeholder) { 19 | scc::type_system::type_registry reg; 20 | auto type = reg.resolve("type"); 21 | ASSERT_EQ(type->kind, scc::type_system::type_kind::UNKNOWN); 22 | } 23 | 24 | TEST(type_registry, types_already_resolve_to_same_backed_instance) { 25 | scc::type_system::type_registry reg; 26 | auto type1 = reg.resolve("type"); 27 | auto type2 = reg.resolve("type"); 28 | 29 | ASSERT_EQ(type1.get(), type2.get()); 30 | } 31 | 32 | TEST(type_registry, types_are_marked_as_existing_when_kind_is_not_unknown) { 33 | scc::type_system::type_registry reg; 34 | auto type = reg.resolve("type"); 35 | type->kind = scc::type_system::type_kind::VALUE; 36 | 37 | ASSERT_TRUE(reg.defined("type")); 38 | } 39 | 40 | 41 | TEST(type_registry, returns_all_registered_types) { 42 | scc::type_system::type_registry reg; 43 | reg.resolve("type"); 44 | 45 | auto res = reg.all_types(); 46 | ASSERT_EQ(res.size(), 1); 47 | ASSERT_EQ(res.front()->name, "type"); 48 | } -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor.cpp: -------------------------------------------------------------------------------- 1 | #include "actor.h" 2 | 3 | #include 4 | 5 | #include "actor_system.h" 6 | #include "actor_type.h" 7 | 8 | namespace vm::actor { 9 | actor::actor(address addr, 10 | address supervisor, 11 | std::unique_ptr initial_state, 12 | std::shared_ptr mailbox, 13 | std::shared_ptr type, 14 | std::shared_ptr system 15 | ) : 16 | self_address(addr), 17 | self_supervisor(supervisor), 18 | self_state(std::move(initial_state)), 19 | bound_mailbox(std::move(mailbox)), 20 | self_type(std::move(type)), 21 | system(std::move(system)) { 22 | } 23 | 24 | actor::~actor() = default; 25 | 26 | void actor::push_from(std::unique_ptr msg) { 27 | bound_mailbox->enqueue(std::move(msg)); 28 | } 29 | 30 | void actor::send(std::unique_ptr msg, address receiver) { 31 | auto trace = vm::mailbox::message_trace { 32 | .stepped_actor = self_address, 33 | .message = msg->message 34 | }; 35 | 36 | msg->traces.push_back(trace); 37 | auto receiver_actor = system->resolve_by_address(receiver); 38 | receiver_actor->push_from(std::move(msg)); 39 | } 40 | 41 | actor_message_process_result actor::process_message(std::unique_ptr message) { 42 | auto name = message->message; 43 | auto call = self_type->resolve(name); 44 | 45 | return call(this, std::move(message)); 46 | } 47 | 48 | std::shared_ptr actor::state_as() { 49 | return this->self_state; 50 | } 51 | } -------------------------------------------------------------------------------- /parser/src/parser_method_call.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-includes/includes.hpp" 3 | 4 | using namespace scc::parser::test; 5 | using namespace scc::ast; 6 | using namespace scc::common; 7 | 8 | using std::get; 9 | 10 | TEST(parser_method_call, reads_a_method_call_from_a_variable) { 11 | const auto result = parse("a.b()"); 12 | const auto mcall = child_nth<0, nmethod_call>(result); 13 | const auto left = left_op(mcall); 14 | 15 | ASSERT_EQ(left->name, "a"); 16 | ASSERT_EQ(mcall->method, "b"); 17 | ASSERT_TRUE(mcall->arguments.empty()); 18 | } 19 | 20 | TEST(parser_method_call, reads_a_method_call_from_a_variable_with_parameters) { 21 | const auto result = parse("a.b(1)"); 22 | const auto mcall = child_nth<0, nmethod_call>(result); 23 | const auto left = left_op(mcall); 24 | 25 | ASSERT_EQ(left->name, "a"); 26 | ASSERT_EQ(mcall->method, "b"); 27 | 28 | auto first_arg = argument_nth<0, nconstant>(mcall); 29 | ASSERT_EQ(first_arg->type, nconstant_type::INTEGER); 30 | ASSERT_EQ(get(first_arg->content).representation, "1"); 31 | } 32 | 33 | TEST(parser_method_call, reads_a_method_call_from_a_variable_with_named_parameters) { 34 | const auto result = parse("a.b(t=42)"); 35 | const auto mcall = child_nth<0, nmethod_call>(result); 36 | const auto left = left_op(mcall); 37 | 38 | ASSERT_EQ(left->name, "a"); 39 | ASSERT_EQ(mcall->method, "b"); 40 | 41 | auto first_arg_named = argument_nth<0, nfunction_call_named_argument>(mcall); 42 | ASSERT_EQ(first_arg_named->name, "t"); 43 | 44 | auto first_arg = argument_value(first_arg_named); 45 | ASSERT_EQ(get(first_arg->content).representation, "42"); 46 | } -------------------------------------------------------------------------------- /discovery/src/discovery.cpp: -------------------------------------------------------------------------------- 1 | #include "discovery.h" 2 | #include "diagnostic.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace scc::discovery { 8 | 9 | discovery::discovery() { 10 | 11 | } 12 | 13 | discovery::~discovery() { 14 | 15 | } 16 | 17 | list discovery::discover_source_files(const list &directories) { 18 | D_START_PHASE(diagnostic::diagnostic_phase_id::DISCOVERY); 19 | list unique = list(directories); 20 | 21 | unique.sort(); 22 | unique.unique(); 23 | 24 | std::ostringstream oss; 25 | copy(unique.begin(), unique.end(), std::ostream_iterator(oss, ",")); 26 | 27 | D_DEBUG("Discovering files in directories. ", { diagnostic::diagnostic_log_marker { .key = "directories", .value = oss.str() }}); 28 | 29 | list paths; 30 | 31 | for (const auto &directory : unique) { 32 | for (const auto& dirEntry : std::filesystem::recursive_directory_iterator(directory)) { 33 | if (dirEntry.path().extension() == ".sn") { 34 | D_DEBUG("Found file. It's going to be processed because it has extension .sn", { diagnostic::diagnostic_log_marker { .key = "file", .value = dirEntry.path() }}); 35 | paths.push_back(dirEntry.path()); 36 | } else if (!dirEntry.is_directory()) { 37 | D_DEBUG("Found file. It's not going to be processed because it doesn't have extension .sn", { diagnostic::diagnostic_log_marker { .key = "file", .value = dirEntry.path() }}); 38 | } 39 | } 40 | } 41 | 42 | paths.sort(); 43 | paths.unique(); 44 | 45 | D_END_PHASE(); 46 | return paths; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../core/address.h" 6 | #include "../mailbox/mailbox.h" 7 | #include "../concurrency/spin_lock.h" 8 | 9 | namespace vm::actor { 10 | using vm::mailbox::message; 11 | using vm::mailbox::mailbox; 12 | using vm::core::address; 13 | 14 | struct actor_type; 15 | struct base_actor_state {}; 16 | class actor_system; 17 | 18 | enum actor_message_process_result { 19 | OK, 20 | FAILED, 21 | }; 22 | 23 | class actor { 24 | public: 25 | actor( 26 | address addr, 27 | address supervisor, 28 | std::unique_ptr initial_state, 29 | std::shared_ptr mailbox, 30 | std::shared_ptr type, 31 | std::shared_ptr system 32 | ); 33 | ~actor(); 34 | 35 | void push_from(std::unique_ptr msg); 36 | void send(std::unique_ptr msg, address receiver); 37 | virtual actor_message_process_result process_message(std::unique_ptr message); 38 | 39 | std::shared_ptr state_as(); 40 | inline address get_address() { 41 | return self_address; 42 | } 43 | 44 | protected: 45 | friend class actor_system; 46 | 47 | std::shared_ptr self_state; 48 | address self_address; 49 | address self_supervisor; 50 | std::shared_ptr self_type; 51 | std::shared_ptr bound_mailbox; 52 | concurrency::spin_lock polling_lock; 53 | concurrency::spin_lock processing_lock; 54 | std::shared_ptr system; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /documentation/welcome/0-hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | Sonata source code is structured in folders and files, that act as limitation of module boundaries. 4 | In this repository there is already a folder sn_examples that contains a few example modules that can 5 | be compiled and played with. 6 | 7 | A Hello World application Sonata looks like: 8 | 9 | ```sn 10 | let extern printf(text: string): none 11 | printf('Hello World') 12 | ``` 13 | 14 | With the expected outcome: 15 | 16 | ```Hello World``` 17 | 18 | This program shows some basic syntax blocks in Sonata. Let's go line by line: 19 | 20 | ```sn 21 | let extern printf(text: string): nothing 22 | ``` 23 | 24 | Let expressions allow to define functions in Sonata. Let functions marked as "extern" are external 25 | native code that will be linked during compilation time. In this example, we declare that we depend 26 | on the standard's library printf function. 27 | 28 | Functions can have parameters: in this case, the text parameter that is a string. For most common cases, 29 | the compiler is smart enough to transform from Sonata types to C types by itself. Custom mappings can be 30 | defined, but are out of scope of this tutorial. 31 | 32 | Functions have a mandatory return type. In this example, the type `nothing`. The type `nothing` can be though 33 | as a the `void` type in other languages. `nothing` means that the function would not return anything important. 34 | 35 | ```sn 36 | printf('Hello World') 37 | ``` 38 | 39 | In Sonata, all code in the global scope is considered code to be executed at the start of the program. Sonata only 40 | guarantees that code in the same file will be executed in order. Code in between files do not necessarily need to be executed 41 | in order and global variables can not be shared (they are essentially private to the file). -------------------------------------------------------------------------------- /ast/src/nlet.cpp: -------------------------------------------------------------------------------- 1 | #include "nlet.h" 2 | 3 | namespace scc::ast { 4 | nlet::nlet() : mutable_p(false) {}; 5 | nlet::~nlet() = default; 6 | 7 | void nlet::to_json(json &j) { 8 | j["id"] = this->id; 9 | j["node"] = "let"; 10 | j["mutable"] = this->mutable_p; 11 | j["constraints"] = this->constraints; 12 | 13 | if (this->expression.has_value()) { 14 | json inner; 15 | this->expression.value()->to_json(inner); 16 | j["expression"] = inner; 17 | } else { 18 | j["expression"] = nullptr; 19 | } 20 | 21 | } 22 | 23 | nlet_function::nlet_function() = default; 24 | nlet_function::~nlet_function() = default; 25 | 26 | void nlet_function::to_json(json &j) { 27 | j["id"] = this->id; 28 | j["node"] = "let function"; 29 | j["return_type"] = this->return_type; 30 | std::vector params; 31 | 32 | for (const auto ¶m : this->parameters) { 33 | json pj; 34 | 35 | if (std::holds_alternative(param)) { 36 | const auto &v = std::get(param); 37 | 38 | pj["kind"] = "named"; 39 | pj["name"] = v->name; 40 | pj["type"] = v->type; 41 | } else { 42 | const auto &v = std::get(param); 43 | pj["kind"] = "expression"; 44 | v->to_json(pj); 45 | } 46 | 47 | params.emplace_back(pj); 48 | } 49 | j["parameters"] = params; 50 | 51 | if (this->body.has_value()) { 52 | json inner; 53 | this->body.value()->to_json(inner); 54 | j["body"] = inner; 55 | } else { 56 | j["body"] = nullptr; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /sn_examples/playground/extern/printbb.c: -------------------------------------------------------------------------------- 1 | #include "printbb.h" 2 | 3 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 4 | 5 | #include 6 | #include 7 | 8 | void printbb(unsigned int *bb, char *b1, char *b2, char *b3, char *b4, char *b5, char *b6, char *b7, char *b8) { 9 | unsigned int padlen = MAX(strlen(b1), strlen(b2)); 10 | unsigned int rowlen = 0; 11 | 12 | padlen = MAX(padlen, strlen(b3)); 13 | padlen = MAX(padlen, strlen(b4)); 14 | padlen = MAX(padlen, strlen(b5)); 15 | padlen = MAX(padlen, strlen(b6)); 16 | padlen = MAX(padlen, strlen(b7)); 17 | padlen = MAX(padlen, strlen(b8)); 18 | 19 | rowlen += (padlen * 8) + (3 * 8) + 1; 20 | 21 | for (int i = 0; i < rowlen; i++) { printf("-"); } 22 | printf("\n"); 23 | 24 | printf("| %0*s ", padlen, b1); 25 | printf("| %0*s ", padlen, b2); 26 | printf("| %0*s ", padlen, b3); 27 | printf("| %0*s ", padlen, b4); 28 | printf("| %0*s ", padlen, b5); 29 | printf("| %0*s ", padlen, b6); 30 | printf("| %0*s ", padlen, b7); 31 | printf("| %0*s |\n", padlen, b8); 32 | 33 | unsigned v = (*bb); 34 | char b8v = v & 0x80 ? '1' : '0'; 35 | char b7v = v & 0x40 ? '1' : '0'; 36 | char b6v = v & 0x20 ? '1' : '0'; 37 | char b5v = v & 0x10 ? '1' : '0'; 38 | char b4v = v & 0x08 ? '1' : '0'; 39 | char b3v = v & 0x04 ? '1' : '0'; 40 | char b2v = v & 0x02 ? '1' : '0'; 41 | char b1v = v & 0x01 ? '1' : '0'; 42 | 43 | printf("| %0*c ", padlen, b1v); 44 | printf("| %0*c ", padlen, b2v); 45 | printf("| %0*c ", padlen, b3v); 46 | printf("| %0*c ", padlen, b4v); 47 | printf("| %0*c ", padlen, b5v); 48 | printf("| %0*c ", padlen, b6v); 49 | printf("| %0*c ", padlen, b7v); 50 | printf("| %0*c |\n", padlen, b8v); 51 | 52 | for (int i = 0; i < rowlen; i++) { printf("-"); } 53 | printf("\n"); 54 | 55 | } -------------------------------------------------------------------------------- /backend-llvm/src/backend.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "llvm/ADT/APFloat.h" 4 | #include "llvm/ADT/Optional.h" 5 | #include "llvm/ADT/STLExtras.h" 6 | #include "llvm/IR/BasicBlock.h" 7 | #include "llvm/IR/Constants.h" 8 | #include "llvm/IR/DerivedTypes.h" 9 | #include "llvm/IR/Function.h" 10 | #include "llvm/IR/IRBuilder.h" 11 | #include "llvm/IR/Instructions.h" 12 | #include "llvm/IR/LLVMContext.h" 13 | #include "llvm/IR/LegacyPassManager.h" 14 | #include "llvm/IR/Module.h" 15 | #include "llvm/IR/Type.h" 16 | #include "llvm/IR/Verifier.h" 17 | #include "llvm/Support/TargetRegistry.h" 18 | #include "llvm/Support/FileSystem.h" 19 | #include "llvm/Support/Host.h" 20 | #include "llvm/Support/TargetSelect.h" 21 | #include "llvm/Support/raw_ostream.h" 22 | #include "llvm/Target/TargetMachine.h" 23 | #include "llvm/Target/TargetOptions.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "ir_builder.h" 38 | #include "ast.h" 39 | 40 | namespace scc::backend::llvm { 41 | using ::llvm::LLVMContext; 42 | using ::llvm::IRBuilder; 43 | using ::llvm::Module; 44 | using ::llvm::TargetMachine; 45 | using ::llvm::legacy::FunctionPassManager; 46 | using std::shared_ptr; 47 | using scc::ast::ast_root; 48 | 49 | class llvm_backend { 50 | public: 51 | explicit llvm_backend(std::shared_ptr &sonata_types); 52 | void write(const ast_root &document); 53 | private: 54 | shared_ptr _context; 55 | shared_ptr> _builder; 56 | shared_ptr _module; 57 | shared_ptr _target_machine; 58 | shared_ptr _pass_manager; 59 | shared_ptr _ir_builder; 60 | }; 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /passes/src/analyzers/pass_detect_classes.cpp: -------------------------------------------------------------------------------- 1 | #include "pass_detect_classes.h" 2 | #include "ast.h" 3 | 4 | static scc::type_system::type_kind map_ast_type(scc::ast::nclass_type type) { 5 | switch (type) { 6 | case scc::ast::nclass_type::VALUE: 7 | return scc::type_system::type_kind::VALUE; 8 | case scc::ast::nclass_type::ENTITY: 9 | case scc::ast::nclass_type::ENTITY_INTERFACE: 10 | return scc::type_system::type_kind::ENTITY; 11 | case scc::ast::nclass_type::CAPABILITY: 12 | return scc::type_system::type_kind::CAPABILITY; 13 | } 14 | 15 | return scc::type_system::type_kind::VALUE; 16 | } 17 | 18 | namespace scc::passes::analyzers { 19 | 20 | pass_detect_classes::pass_detect_classes(const std::shared_ptr &types) 21 | : types(types) { 22 | 23 | } 24 | 25 | pass_detect_classes::~pass_detect_classes() = default; 26 | 27 | void pass_detect_classes::execute(ast::ast_root &root) const { 28 | for (auto &child : root->children) { 29 | if (std::dynamic_pointer_cast(child)) { 30 | auto nclass = std::dynamic_pointer_cast(child); 31 | auto type = types->resolve(nclass->name); 32 | 33 | type->kind = map_ast_type(nclass->type); 34 | 35 | for (auto &f : nclass->fields) { 36 | auto base_type = types->resolve(std::get(f->type).type); 37 | auto zero = memory::selector { .type = type_system::memory::selector_type::DIRECT, .offset = 0 }; 38 | 39 | type->fields.emplace_back(std::make_shared(field { .base_type = base_type, .name = f->name, .selector = zero})); 40 | } 41 | } 42 | } 43 | } 44 | 45 | diagnostic::diagnostic_phase_id pass_detect_classes::pass_phase() const { 46 | return diagnostic::diagnostic_phase_id::PASS_DETECT_CLASSES; 47 | } 48 | } -------------------------------------------------------------------------------- /documentation/advanced/memory-model/0-memory-layout-isolation.md: -------------------------------------------------------------------------------- 1 | # Memory Layout Isolation 2 | 3 | Sonata has two encapsulation boundaries at the language level. Value classes 4 | and entity classes. They serve two completely different use cases, and thus, 5 | can have different ABIs. 6 | 7 | One of the main concerns of current ABIs is that memory is public available to the module 8 | and can be accessed freely. For example, in most ABIs, structs need to have a specific layout so public 9 | modules can read and mutate the public state of the struct. 10 | 11 | Also, ABIs are different depending on the hardware and operating system, adding complexity and 12 | making optimizations more complicated for the compiler. 13 | 14 | The Sonata compiler exposes an ABI, as any compiler, but internally it follows an internal modeling mechanism 15 | that is used to optimize memory access patterns specific to Sonata. 16 | 17 | Sonata is based on entities that are enforced encapsulation units: 18 | 19 | * Entity state is private to the instance of the entity 20 | * Entities are single-threaded 21 | * Entities are mutable only within the entity context 22 | 23 | So, for example, consider the following code: 24 | 25 | ```sn 26 | value class AccountBalance(account: string, balance: double) 27 | 28 | entity class Account(name: string, balance: double) { 29 | let deposit(amount: double) = self.balance += amount 30 | let withdraw(amount: double) = self.balance -= amount 31 | let withdraw(amount > self.balance) = nothing 32 | 33 | let balance() = AccountBalance(self.name, self.balance) 34 | } 35 | ``` 36 | 37 | Thanks to Sonata encapsulation boundaries, we can do the following assumptions: 38 | 39 | * name is effectively immutable 40 | * balance is only going to be modified by a single thread 41 | * State is shared only by a single public mechanism: public method balance 42 | 43 | So with mutation and query rules being clear, we can optimise these access patterns and separate the ABI 44 | from the internal memory model. 45 | 46 | ![Memory Model Boundaries](img/0-memory-model-boundaries-ABI.svg) -------------------------------------------------------------------------------- /parser/src/parser_type.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-includes/includes.hpp" 3 | 4 | using namespace scc::parser::test; 5 | using namespace scc::ast; 6 | using namespace scc::common; 7 | 8 | using std::get; 9 | 10 | TEST(parser_type, alias_a_type) { 11 | const auto result = parse("type text = string"); 12 | const auto type = child_nth<0, ntype>(result); 13 | 14 | ASSERT_EQ(type->name, "text"); 15 | ASSERT_EQ(get(type->constraints).type, "string"); 16 | } 17 | 18 | TEST(parser_type, alias_a_generic_type) { 19 | const auto result = parse("type ints = list[int]"); 20 | const auto type = child_nth<0, ntype>(result); 21 | 22 | ASSERT_EQ(type->name, "ints"); 23 | ASSERT_EQ(get(type->constraints).base, "list"); 24 | auto genpar0 = generic_parameter_nth<0, type_constraint_equality>(type->constraints); 25 | ASSERT_EQ(genpar0.type, "int"); 26 | } 27 | 28 | TEST(parser_type, defines_a_constant_string_type) { 29 | const auto result = parse("type undefined = 'undefined'"); 30 | const auto type = child_nth<0, ntype>(result); 31 | 32 | ASSERT_EQ(type->name, "undefined"); 33 | ASSERT_EQ(get(type->constraints).value, "undefined"); 34 | ASSERT_EQ(get(type->constraints).base_constraint.type, "string"); 35 | } 36 | 37 | 38 | TEST(parser_type, defines_a_constant_floating_type) { 39 | const auto result = parse("type pi = 3.14"); 40 | const auto type = child_nth<0, ntype>(result); 41 | 42 | ASSERT_EQ(type->name, "pi"); 43 | ASSERT_EQ(get(type->constraints).value, "3.14"); 44 | ASSERT_EQ(get(type->constraints).base_constraint.type, "number"); 45 | } 46 | 47 | TEST(parser_type, defines_a_constant_integer_type) { 48 | const auto result = parse("type ten = 10"); 49 | const auto type = child_nth<0, ntype>(result); 50 | 51 | ASSERT_EQ(type->name, "ten"); 52 | ASSERT_EQ(get(type->constraints).value, "10"); 53 | ASSERT_EQ(get(type->constraints).base_constraint.type, "number"); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /runtime/vm/src/api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "./mailbox/mailbox.h" 4 | #include "./mailbox/fiber.h" 5 | #include "./core/address.h" 6 | #include "./actor/actor.h" 7 | #include "./actor/actor_type.h" 8 | #include "./actor/actor_system.h" 9 | 10 | namespace vma = vm::actor; 11 | namespace vmm = vm::mailbox; 12 | namespace vmc = vm::core; 13 | 14 | /** 15 | * Sonata Runtime API 16 | * 17 | * This is an stable API that can be used within the compiler when generating code. 18 | * This API can be linked both statically and dynamically with the application. 19 | * 20 | * Do not use this API directly: it's designed only for the compiler. 21 | * If you want to integrate with the Sonata runtime at this level, use the C++ APIs. 22 | */ 23 | 24 | extern "C" { 25 | typedef uint8_t thread_count_t; 26 | 27 | // Actor API 28 | vmc::address mkaddress(); 29 | vma::actor *mkactor(vmc::address addr, vmc::address supervisor, vma::base_actor_state *state, vmm::mailbox *mb, vma::actor_type *type); 30 | void dlactor(vma::actor *actor); 31 | void actor_receive(vma::actor *rcv, vmm::message *msg); 32 | void actor_send(vma::actor *actor, vmm::message *msg, vmc::address &rcv); 33 | void *actor_state(vma::actor *actor); // actor state is only "typed" at the C++ level. 34 | 35 | // Actor System API 36 | vma::actor_system *mkactorsystem(thread_count_t count); 37 | vma::actor_system *getactorsystem(); 38 | vmm::mailbox *getmailbox(); 39 | void dlactorsystem(); 40 | vma::actor *actorsystem_resolve_by_address(const vmc::address &addr); 41 | void actorsystem_register_actor(vma::actor *actor); 42 | 43 | // Actor Type API 44 | vma::actor_type *mkactortype(const char *name); 45 | void actortype_register(vma::actor_type *type, const char *msg, const vma::dispatch_message &dispatch); 46 | 47 | // Message API 48 | vmm::message *mkmsg(vma::actor *sender, const char *name); 49 | void message_set_metadata(vmm::message *msg, const char *name, const char *value); 50 | void message_push_arg(vmm::message *msg, const char *name); 51 | void *message_pop_arg(vmm::message *msg); 52 | void message_send(vma::actor *sender, vmm::message *msg, vma::address &address); 53 | } -------------------------------------------------------------------------------- /runtime/vm/src/mailbox/fiber.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "../actor/actor.h" 6 | #include "../actor/actor_system.h" 7 | #include "fiber.h" 8 | 9 | using namespace std::chrono_literals; 10 | 11 | struct actor_state : public vm::actor::base_actor_state { 12 | bool did_run; 13 | }; 14 | 15 | class fake_actor : public vm::actor::actor { 16 | public: 17 | fake_actor( 18 | vm::core::address addr, vm::core::address supervisor, 19 | std::unique_ptr initial_state, 20 | std::shared_ptr mailbox, 21 | std::shared_ptr type, 22 | std::shared_ptr system 23 | ) : actor(addr, supervisor, std::move(initial_state), std::move(mailbox), std::move(type), std::move(system)) { 24 | } 25 | 26 | vm::actor::actor_message_process_result process_message(std::unique_ptr message) override { 27 | auto ref = this->processing_lock.lock(); 28 | auto state = std::reinterpret_pointer_cast(this->state_as()); 29 | state->did_run = true; 30 | return vm::actor::OK; 31 | } 32 | }; 33 | 34 | TEST(fiber, processes_a_message_for_an_actor) { 35 | auto system = std::make_shared(); 36 | auto mb = std::make_shared(); 37 | auto mock = std::make_shared( 38 | vm::core::make_address(), 39 | vm::core::make_address(), 40 | std::make_unique(actor_state { .did_run = false }), 41 | mb, 42 | nullptr, 43 | system 44 | ); 45 | 46 | auto always_mock = [mock](vm::actor::address addr) -> std::shared_ptr { 47 | return mock; 48 | }; 49 | 50 | auto fb = std::make_shared(mb, always_mock); 51 | 52 | auto enqueued_message = std::make_unique(); 53 | mock->push_from(std::move(enqueued_message)); 54 | 55 | std::this_thread::sleep_for(100us); 56 | fb->stop(); 57 | 58 | auto state = std::reinterpret_pointer_cast(mock->state_as()); 59 | ASSERT_EQ(state->did_run, true); 60 | } -------------------------------------------------------------------------------- /ast/src/ir/nstruct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../node.h" 4 | 5 | namespace scc::ast::ir { 6 | enum nstruct_field_type { 7 | BIT_BAG, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING, OBJECT, VOID 8 | }; 9 | 10 | struct nstruct_field { 11 | nstruct_field_type field_type; 12 | std::string name; 13 | unsigned int size; // for when applies 14 | }; 15 | 16 | struct nstruct : public node { 17 | std::string name; 18 | std::list fields; 19 | 20 | void to_json(json &j) override; 21 | }; 22 | 23 | struct nstruct_malloc : public expression { 24 | std::string type; 25 | 26 | void to_json(json &j) override; 27 | }; 28 | 29 | struct nstruct_free : public expression { 30 | void to_json(json &j) override; 31 | }; 32 | 33 | struct nstruct_register_to_mailbox : public expression { 34 | expression_ref self; 35 | 36 | void to_json(json &j) override; 37 | }; 38 | 39 | struct nstruct_function_arg { 40 | std::string name; 41 | nstruct_field_type type; 42 | }; 43 | 44 | struct nstruct_function_def : public expression { 45 | std::string struct_name; 46 | std::string name; 47 | std::list signature; 48 | nstruct_field_type retval; 49 | ast_block body; 50 | 51 | void to_json(json &j) override; 52 | }; 53 | 54 | struct nstruct_function_call : public expression { 55 | expression_ref expression; 56 | std::string name; 57 | std::list arguments; 58 | 59 | void to_json(json &j) override; 60 | }; 61 | 62 | struct nstruct_bitbag_set : public expression { 63 | unsigned int bit; 64 | expression_ref value; 65 | 66 | void to_json(json &j) override; 67 | }; 68 | 69 | struct nstruct_direct_set : public expression { 70 | unsigned int index; 71 | expression_ref value; 72 | 73 | void to_json(json &j) override; 74 | }; 75 | 76 | struct nstruct_bitbag_get : public expression { 77 | unsigned int bit; 78 | void to_json(json &j) override; 79 | }; 80 | 81 | struct nstruct_direct_get : public expression { 82 | unsigned int index; 83 | void to_json(json &j) override; 84 | }; 85 | } -------------------------------------------------------------------------------- /parser/src/parser_spawn.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-includes/includes.hpp" 3 | 4 | using namespace scc::parser::test; 5 | using namespace scc::ast; 6 | using namespace scc::common; 7 | 8 | using std::get; 9 | 10 | TEST(parser_spawn, spawns_an_entity) { 11 | const auto result = parse("spawn A()"); 12 | const auto nspawn = child_nth<0, nspawn_entity>(result); 13 | 14 | ASSERT_EQ(nspawn->entity_name, "A"); 15 | ASSERT_TRUE(nspawn->arguments.empty()); 16 | } 17 | 18 | 19 | TEST(parser_spawn, reads_a_spawn_with_a_single_parameter) { 20 | const auto result = parse("spawn WithANumber(1)"); 21 | const auto fncall = child_nth<0, nspawn_entity>(result); 22 | const auto entity = fncall->entity_name; 23 | const auto param = argument_nth<0, nconstant>(fncall); 24 | 25 | ASSERT_EQ(entity, "WithANumber"); 26 | ASSERT_EQ(fncall->arguments.size(), 1); 27 | ASSERT_EQ(param->type, nconstant_type::INTEGER); 28 | ASSERT_EQ(get(param->content).representation, "1"); 29 | } 30 | 31 | TEST(parser_spawn, reads_a_spawn_expression_with_multiple_parameters) { 32 | const auto result = parse("spawn WithMultipleNumbers(1, 2)"); 33 | const auto fncall = child_nth<0, nspawn_entity>(result); 34 | const auto entity = fncall->entity_name; 35 | const auto param1 = argument_nth<0, nconstant>(fncall); 36 | const auto param2 = argument_nth<1, nconstant>(fncall); 37 | 38 | ASSERT_EQ(entity, "WithMultipleNumbers"); 39 | ASSERT_EQ(fncall->arguments.size(), 2); 40 | ASSERT_EQ(param1->type, nconstant_type::INTEGER); 41 | ASSERT_EQ(get(param1->content).representation, "1"); 42 | ASSERT_EQ(param2->type, nconstant_type::INTEGER); 43 | ASSERT_EQ(get(param2->content).representation, "2"); 44 | } 45 | 46 | TEST(parser_spawn, reads_a_spawn_expression_with_a_named_argument) { 47 | const auto result = parse("spawn WithNamedArgument(a=1)"); 48 | const auto fncall = child_nth<0, nspawn_entity>(result); 49 | const auto entity = fncall->entity_name; 50 | const auto param1 = argument_nth<0, nfunction_call_named_argument>(fncall); 51 | const auto param1_value = argument_value(param1); 52 | 53 | ASSERT_EQ(entity, "WithNamedArgument"); 54 | ASSERT_EQ(fncall->arguments.size(), 1); 55 | ASSERT_EQ(param1->name, "a"); 56 | ASSERT_EQ(param1_value->type, nconstant_type::INTEGER); 57 | ASSERT_EQ(get(param1_value->content).representation, "1"); 58 | } -------------------------------------------------------------------------------- /type-system/src/type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace scc::type_system { 10 | namespace memory { 11 | enum class layout_type : unsigned char { 12 | NONE, STATIC, FLEXIBLE 13 | }; 14 | 15 | enum class bit_bag_reservation_type : unsigned char { 16 | BOOLEAN, ENUM 17 | }; 18 | 19 | enum class selector_type : unsigned char { 20 | BIT_BAG, DIRECT 21 | }; 22 | 23 | struct big_bag_reservation_enum_translation { 24 | unsigned int enum_value; 25 | unsigned int bit; 26 | bool value; 27 | }; 28 | 29 | struct bit_bag_reservation { 30 | unsigned int bits; 31 | bit_bag_reservation_type type; 32 | std::vector translations; 33 | }; 34 | 35 | struct bit_bag { 36 | unsigned int size; 37 | std::vector reservations; 38 | }; 39 | 40 | struct direct_mapping { 41 | unsigned int size; 42 | }; 43 | 44 | struct reference { 45 | }; 46 | 47 | struct padding { 48 | unsigned int size; 49 | }; 50 | 51 | typedef std::variant memory_storage; 52 | 53 | struct layout { 54 | unsigned int size_in_bytes; 55 | layout_type type; 56 | std::vector storages; 57 | }; 58 | 59 | struct selector { 60 | selector_type type; 61 | unsigned int offset; 62 | }; 63 | } 64 | 65 | enum class type_kind : unsigned char { 66 | UNKNOWN, PRIMITIVE, VALUE, ENTITY, CAPABILITY 67 | }; 68 | 69 | struct type; 70 | struct field; 71 | struct method; 72 | 73 | struct type { 74 | type_kind kind; 75 | std::string name; 76 | std::shared_ptr parent; 77 | memory::layout layout; 78 | std::vector> fields; 79 | std::vector> methods; 80 | }; 81 | 82 | struct field { 83 | std::shared_ptr base_type; 84 | std::string name; 85 | memory::selector selector; 86 | }; 87 | 88 | struct method { 89 | std::shared_ptr return_type; 90 | std::vector>> parameters_types; 91 | std::string name; 92 | }; 93 | } -------------------------------------------------------------------------------- /ast/src/node.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace scc::ast { 11 | using std::string; 12 | using std::variant; 13 | using std::list; 14 | using json = nlohmann::json; 15 | using std::optional; 16 | 17 | struct node_id { 18 | uint32_t id; 19 | string source; 20 | uint32_t row, column; 21 | }; 22 | 23 | struct type_constraint_none {}; 24 | struct type_constraint_sum; 25 | struct type_constraint_equality { string type; }; 26 | struct type_constraint_constant { 27 | type_constraint_equality base_constraint; 28 | string value; 29 | }; 30 | struct type_constraint_generic; 31 | struct type_constraint_sum; 32 | 33 | typedef variant type_constraints; 34 | 35 | struct type_constraint_generic { 36 | string base; 37 | list parameters; 38 | }; 39 | struct type_constraint_sum { 40 | list types; 41 | }; 42 | 43 | struct node { 44 | node_id id; 45 | 46 | virtual void to_json(json& j) = 0; 47 | }; 48 | 49 | typedef std::string type_name; 50 | struct expression : public node { 51 | type_constraints constraints; 52 | type_name analysed_type; 53 | }; 54 | 55 | typedef std::shared_ptr node_ref; 56 | typedef std::shared_ptr expression_ref; 57 | 58 | struct node_qualified_context { 59 | list name; 60 | 61 | void to_json(json &j); 62 | }; 63 | 64 | struct root : node { 65 | list children; 66 | node_qualified_context context; 67 | 68 | void to_json(json &j) override; 69 | }; 70 | 71 | struct block : node { 72 | list children; 73 | 74 | void to_json(json &j) override; 75 | }; 76 | 77 | typedef std::shared_ptr ast_root; 78 | typedef std::shared_ptr ast_block; 79 | 80 | void to_json(json& j, const node_id& id); 81 | void to_json(json& j, const type_constraint_none& tcnone); 82 | void to_json(json& j, const type_constraint_sum& tcsum); 83 | void to_json(json& j, const type_constraint_equality& tceq); 84 | void to_json(json& j, const type_constraint_constant& tconstant); 85 | void to_json(json& j, const type_constraint_generic& tcgen); 86 | void to_json(json& j, const type_constraints & tconstraints); 87 | } -------------------------------------------------------------------------------- /passes/src/mutations/pass_entity_class_method_resolution.cpp: -------------------------------------------------------------------------------- 1 | #include "pass_entity_class_method_resolution.h" 2 | 3 | namespace scc::passes::mutations { 4 | using namespace scc::ast; 5 | using namespace scc::ast::ir; 6 | 7 | pass_entity_class_method_resolution::pass_entity_class_method_resolution(const std::shared_ptr &types) 8 | : types(types) { 9 | 10 | } 11 | 12 | pass_entity_class_method_resolution::~pass_entity_class_method_resolution() = default; 13 | 14 | static node_ref recursive_iterate(node_ref node) { 15 | if (std::dynamic_pointer_cast(node)) { 16 | auto mcall = std::dynamic_pointer_cast(node); 17 | auto nfcall = std::make_shared(); 18 | 19 | auto fcallid = std::make_shared(); 20 | fcallid->name = mcall->entity_name + "_spawn"; 21 | 22 | nfcall->left = fcallid; 23 | nfcall->arguments.insert(nfcall->arguments.begin(), mcall->arguments.cbegin(), mcall->arguments.cend()); 24 | 25 | return nfcall; 26 | } else if (std::dynamic_pointer_cast(node)) { 27 | auto mcall = std::dynamic_pointer_cast(node); 28 | auto nfcall = std::make_shared(); 29 | 30 | auto fcallid = std::make_shared(); 31 | fcallid->name = "EntityTest_" + mcall->method; 32 | 33 | nfcall->left = fcallid; 34 | nfcall->arguments.insert(nfcall->arguments.begin(), mcall->arguments.cbegin(), mcall->arguments.cend()); 35 | 36 | nfcall->arguments.push_front(mcall->left); 37 | return nfcall; 38 | } else if (std::dynamic_pointer_cast(node)) { 39 | auto mlet = std::dynamic_pointer_cast(node); 40 | if (mlet->expression.has_value()) { 41 | auto old_expr = mlet->expression.value(); 42 | auto new_expr = recursive_iterate(old_expr); 43 | mlet->expression = std::dynamic_pointer_cast(new_expr); 44 | } 45 | } 46 | 47 | return node; 48 | } 49 | 50 | void pass_entity_class_method_resolution::execute(ast::ast_root &root) const { 51 | list new_children; 52 | 53 | for (auto &child : root->children) { 54 | new_children.push_back(recursive_iterate(child)); 55 | } 56 | 57 | root->children = new_children; 58 | } 59 | 60 | diagnostic::diagnostic_phase_id pass_entity_class_method_resolution::pass_phase() const { 61 | return diagnostic::diagnostic_phase_id::PASS_ENTITY_METHOD_RESOLUTION; 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /lexer/src/token_type.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | 3 | namespace scc::lexer { 4 | std::string to_string(const token_type &type) { 5 | switch (type) { 6 | case token_type::COMMENT: 7 | return "COMMENT"; 8 | case token_type::WHITESPACE: 9 | return "WHITESPACE"; 10 | case token_type::NEW_LINE: 11 | return "NEW_LINE"; 12 | case token_type::END_OF_FILE: 13 | return "END_OF_FILE"; 14 | 15 | case token_type::IDENTIFIER: 16 | return "IDENTIFIER"; 17 | case token_type::INTEGER: 18 | return "INTEGER"; 19 | case token_type::FLOATING: 20 | return "FLOATING"; 21 | 22 | case token_type::COLON: 23 | return "COLON"; 24 | case token_type::COMMA: 25 | return "COMMA"; 26 | case token_type::DOT: 27 | return "DOT"; 28 | 29 | case token_type::OPEN_PAREN: 30 | return "OPEN_PAREN"; 31 | case token_type::OPEN_BRACKET: 32 | return "OPEN_BRACKET"; 33 | case token_type::OPEN_BRACE: 34 | return "OPEN_BRACE"; 35 | case token_type::CLOSE_PAREN: 36 | return "CLOSE_PAREN"; 37 | case token_type::CLOSE_BRACKET: 38 | return "CLOSE_BRACKET"; 39 | case token_type::CLOSE_BRACE: 40 | return "CLOSE_BRACE"; 41 | case token_type::STRING: 42 | return "STRING"; 43 | 44 | case token_type::AT: 45 | return "AT"; 46 | case token_type::HASH: 47 | return "HASH"; 48 | case token_type::TILDE: 49 | return "TILDE"; 50 | case token_type::QUESTION_MARK: 51 | return "QUESTION_MARK"; 52 | case token_type::EXCLAMATION_MARK: 53 | return "EXCLAMATION_MARK"; 54 | case token_type::EQUALS: 55 | return "EQUALS"; 56 | case token_type::LESS_THAN: 57 | return "LESS_THAN"; 58 | case token_type::GREATER_THAN: 59 | return "GREATER_THAN"; 60 | case token_type::PLUS: 61 | return "PLUS"; 62 | case token_type::MINUS: 63 | return "MINUS"; 64 | case token_type::MULTIPLY: 65 | return "MULTIPLY"; 66 | case token_type::DIVIDE: 67 | return "DIVIDE"; 68 | case token_type::CARET: 69 | return "CARET"; 70 | case token_type::PERCENT: 71 | return "PERCENT"; 72 | case token_type::AMPERSAND: 73 | return "AMPERSAND"; 74 | case token_type::UNKNOWN: 75 | return "UNKNOWN"; 76 | } 77 | 78 | return "UNKNOWN"; 79 | } 80 | } -------------------------------------------------------------------------------- /.github/workflows/health.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | BuildProject: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Install Build Essentials 8 | run: | 9 | sudo apt update -y 10 | sudo apt install -y llvm llvm-dev clang libgtest-dev nlohmann-json3-dev cmake 11 | 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Build cmake Project 16 | run: cmake -S . -B cmake-build/ 17 | 18 | - name: Compile Project 19 | run: | 20 | export CC=/usr/bin/clang 21 | export CXX=/usr/bin/clang++ 22 | 23 | cd cmake-build 24 | make all 25 | cd ../ 26 | 27 | - uses: actions/upload-artifact@master 28 | with: 29 | name: cmake-build 30 | path: cmake-build 31 | 32 | RunLexerTests: 33 | runs-on: ubuntu-latest 34 | needs: BuildProject 35 | steps: 36 | - name: Download Build 37 | uses: actions/download-artifact@master 38 | with: 39 | name: cmake-build 40 | path: . 41 | 42 | - name: Run Tests 43 | run: | 44 | chmod +x ./lexer/lexer_tests 45 | ./lexer/lexer_tests 46 | 47 | RunParserTests: 48 | runs-on: ubuntu-latest 49 | needs: BuildProject 50 | steps: 51 | - name: Download Build 52 | uses: actions/download-artifact@master 53 | with: 54 | name: cmake-build 55 | path: . 56 | 57 | - name: Run Tests 58 | run: | 59 | chmod +x ./parser/parser_tests 60 | ./parser/parser_tests 61 | 62 | RunDiagnosticTests: 63 | runs-on: ubuntu-latest 64 | needs: BuildProject 65 | steps: 66 | - name: Download Build 67 | uses: actions/download-artifact@master 68 | with: 69 | name: cmake-build 70 | path: . 71 | 72 | - name: Run Tests 73 | run: | 74 | chmod +x ./diagnostic/diagnostic_tests 75 | ./diagnostic/diagnostic_tests 76 | 77 | RunTypeSystemTests: 78 | runs-on: ubuntu-latest 79 | needs: BuildProject 80 | steps: 81 | - name: Download Build 82 | uses: actions/download-artifact@master 83 | with: 84 | name: cmake-build 85 | path: . 86 | 87 | - name: Run Tests 88 | run: | 89 | chmod +x ./type-system/type-system_tests 90 | ./type-system/type-system_tests 91 | 92 | RunSonataRuntimeTests: 93 | runs-on: ubuntu-latest 94 | needs: BuildProject 95 | steps: 96 | - name: Download Build 97 | uses: actions/download-artifact@master 98 | with: 99 | name: cmake-build 100 | path: . 101 | 102 | - name: Run Tests 103 | run: | 104 | chmod +x ./runtime/vm/sonata-rt_tests 105 | ./runtime/vm/sonata-rt_tests -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lexer/src/lexer.h" 6 | #include "diagnostic/src/diagnostic.h" 7 | #include "discovery/src/discovery.h" 8 | #include "parser/src/parser.h" 9 | #include "backend-llvm/src/backend.h" 10 | #include "passes/src/pass_manager.h" 11 | #include "type-system/src/type_registry.h" 12 | 13 | int main(int argc, char **argv) { 14 | bool diagnostic = false; 15 | std::string diagnostic_file = "scc-diagnostic.json"; 16 | scc::diagnostic::diagnostic_log_level allowed_level = scc::diagnostic::diagnostic_log_level::WARN; 17 | std::list directories_to_process; 18 | int c; 19 | 20 | while ((c = getopt(argc, argv, "D:L:")) != -1) { 21 | switch (c) { 22 | case 'D': 23 | diagnostic = true; 24 | if (std::string(optarg) != "default") { 25 | diagnostic_file = optarg; 26 | if (!diagnostic_file.ends_with(".json")) { 27 | diagnostic_file += ".json"; 28 | } 29 | } 30 | 31 | break; 32 | case 'L': 33 | std::string level = optarg; 34 | if (level == "DEBUG") { 35 | allowed_level = scc::diagnostic::diagnostic_log_level::DEBUG; 36 | } else if (level == "WARN") { 37 | allowed_level = scc::diagnostic::diagnostic_log_level::WARN; 38 | } else if (level == "INFO") { 39 | allowed_level = scc::diagnostic::diagnostic_log_level::INFO; 40 | } else if (level == "ERROR") { 41 | allowed_level = scc::diagnostic::diagnostic_log_level::ERROR; 42 | } else if (level == "FATAL") { 43 | allowed_level = scc::diagnostic::diagnostic_log_level::FATAL; 44 | } 45 | } 46 | } 47 | 48 | for(; optind < argc; optind++){ 49 | directories_to_process.emplace_back(argv[optind]); 50 | } 51 | 52 | if (directories_to_process.empty()) { 53 | directories_to_process.emplace_back("."); 54 | } 55 | 56 | auto types = std::make_shared(); 57 | 58 | scc::diagnostic::initialize(allowed_level); 59 | scc::discovery::discovery discovery; 60 | scc::lexer::lexer lexer; 61 | scc::parser::parser parser; 62 | scc::passes::pass_manager pass_manager(types); 63 | scc::backend::llvm::llvm_backend backend(types); 64 | 65 | std::list files_to_process = discovery.discover_source_files(directories_to_process); 66 | scc::lexer::token_stream all_tokens; 67 | 68 | for (const auto &source_file : files_to_process) { 69 | std::ifstream ifs(source_file); 70 | auto processed = lexer.process(ifs, source_file); 71 | all_tokens.splice(all_tokens.end(), processed); 72 | } 73 | 74 | auto result = parser.parse(all_tokens); 75 | pass_manager.run(result); 76 | 77 | backend.write(result); 78 | 79 | scc::diagnostic::finish(); 80 | scc::diagnostic::print_user_diagnostic(); 81 | 82 | if (diagnostic) { 83 | scc::diagnostic::dump_diagnostic(diagnostic_file); 84 | } 85 | 86 | return scc::diagnostic::return_code(); 87 | } 88 | -------------------------------------------------------------------------------- /ast/src/nclass.cpp: -------------------------------------------------------------------------------- 1 | #include "nconstant.h" 2 | #include "nclass.h" 3 | 4 | 5 | namespace scc::ast { 6 | nconstant::nconstant() = default; 7 | nconstant::~nconstant() = default; 8 | 9 | void nconstant::to_json(json &j) { 10 | j["id"] = this->id; 11 | j["node"] = "constant"; 12 | 13 | if (holds_alternative(this->content)) { 14 | j["value"] = get(this->content); 15 | } 16 | 17 | if (holds_alternative(this->content)) { 18 | j["value"] = get(this->content); 19 | } 20 | 21 | if (holds_alternative(this->content)) { 22 | j["value"] = get(this->content); 23 | } 24 | 25 | if (holds_alternative(this->content)) { 26 | j["value"] = get(this->content); 27 | } 28 | } 29 | 30 | void ast::nclass::to_json(json &j) { 31 | switch (type) { 32 | case nclass_type::CAPABILITY: 33 | j["type"] = "capability"; 34 | break; 35 | case nclass_type::ENTITY: 36 | j["type"] = "entity"; 37 | break; 38 | case nclass_type::VALUE: 39 | j["type"] = "value"; 40 | break; 41 | } 42 | 43 | switch (visibility) { 44 | case nclass_visibility::PUBLIC: 45 | j["visibility"] = "public"; 46 | break; 47 | case nclass_visibility::PRIVATE: 48 | j["visibility"] = "private"; 49 | break; 50 | } 51 | 52 | j["package"] = package; 53 | j["name"] = name; 54 | 55 | std::vector fields_json; 56 | for (const auto &f : fields) { 57 | json fj; 58 | fj["type"] = f->type; 59 | fj["name"] = f->name; 60 | if (f->initial_value.has_value()) { 61 | f->initial_value.value()->to_json(fj["initial_value"]); 62 | } 63 | fields_json.emplace_back(fj); 64 | } 65 | 66 | j["fields"] = fields_json; 67 | std::vector requirements_json; 68 | for (const auto &r : requirements) { 69 | json rj; 70 | rj["field"] = r->field; 71 | rj["capability"] = r->capability; 72 | requirements_json.emplace_back(rj); 73 | } 74 | j["requirements"] = requirements_json; 75 | std::vector allowance_json; 76 | 77 | if (body.has_value()) { 78 | body.value()->to_json(j["body"]); 79 | } 80 | } 81 | 82 | void nclass_self_set::to_json(json &j) { 83 | std::vector js; 84 | json jv; 85 | 86 | for (auto &k : selector) { 87 | js.emplace_back(k); 88 | } 89 | 90 | value->to_json(jv); 91 | 92 | j["selector"] = js; 93 | j["value"] = jv; 94 | } 95 | 96 | void nclass_self_get::to_json(json &j) { 97 | std::vector js; 98 | 99 | for (auto &k : selector) { 100 | js.emplace_back(k); 101 | } 102 | 103 | j["selector"] = js; 104 | } 105 | } -------------------------------------------------------------------------------- /ast/src/node.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "node.h" 4 | 5 | namespace scc::ast { 6 | using std::vector; 7 | 8 | void node_qualified_context::to_json(json &j) { 9 | j["name"] = this->name; 10 | } 11 | 12 | void root::to_json(json &j) { 13 | j["id"] = this->id; 14 | j["type"] = "root"; 15 | 16 | vector childrenj; 17 | for (const auto &node : this->children) { 18 | json cj; 19 | node->to_json(cj); 20 | childrenj.emplace_back(cj); 21 | } 22 | 23 | j["children"] = childrenj; 24 | this->context.to_json(j["context"]); 25 | } 26 | 27 | void block::to_json(json &j) { 28 | j["id"] = this->id; 29 | j["type"] = "block"; 30 | 31 | vector childrenj; 32 | for (const auto &node : this->children) { 33 | json cj; 34 | node->to_json(cj); 35 | childrenj.emplace_back(cj); 36 | } 37 | 38 | j["children"] = childrenj; 39 | } 40 | 41 | void to_json(json &j, const node_id &id) { 42 | j["id"] = id.id; 43 | j["source"] = id.source; 44 | j["row"] = id.row; 45 | j["column"] = id.column; 46 | } 47 | 48 | void to_json(json& j, const type_constraint_none& tcnone) { 49 | j["type_constraint"] = "none"; 50 | } 51 | 52 | void to_json(json& j, const type_constraint_sum& tcsum) { 53 | j["type_constraint"] = "sum"; 54 | std::vector sum; 55 | for (auto const &type : tcsum.types) { 56 | sum.emplace_back(type); 57 | } 58 | j["sum"] = sum; 59 | } 60 | 61 | void to_json(json& j, const type_constraint_equality& tceq) { 62 | j["type_constraint"] = "equals"; 63 | j["equals"] = tceq.type; 64 | } 65 | 66 | void to_json(json& j, const type_constraint_constant& tconstant) { 67 | j["type_constraint"] = "constant"; 68 | j["base"] = tconstant.base_constraint; 69 | j["value"] = tconstant.value; 70 | } 71 | 72 | void to_json(json& j, const type_constraint_generic& tcgen) { 73 | j["type_constraint"] = "generic"; 74 | std::vector params; 75 | for (auto const &type : tcgen.parameters) { 76 | params.emplace_back(type); 77 | } 78 | j["parameters"] = params; 79 | } 80 | 81 | void to_json(json& j, const type_constraints& tconstraints) { 82 | if (std::holds_alternative(tconstraints)) { 83 | j = std::get(tconstraints); 84 | } 85 | 86 | if (std::holds_alternative(tconstraints)) { 87 | j = std::get(tconstraints); 88 | } 89 | 90 | if (std::holds_alternative(tconstraints)) { 91 | j = std::get(tconstraints); 92 | } 93 | 94 | if (std::holds_alternative(tconstraints)) { 95 | j = std::get(tconstraints); 96 | } 97 | 98 | if (std::holds_alternative(tconstraints)) { 99 | j = std::get(tconstraints); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /parser/src/parser_function_call.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-includes/includes.hpp" 3 | 4 | using namespace scc::parser::test; 5 | using namespace scc::ast; 6 | using namespace scc::common; 7 | 8 | using std::get; 9 | 10 | TEST(parser_function_call, reads_a_function_call_without_parameters) { 11 | const auto result = parse("withNoParams()"); 12 | const auto fncall = child_nth<0, nfunction_call>(result); 13 | const auto left = left_op(fncall); 14 | 15 | ASSERT_EQ(left->name, "withNoParams"); 16 | ASSERT_TRUE(fncall->arguments.empty()); 17 | } 18 | 19 | TEST(parser_function_call, reads_a_function_call_with_a_single_parameter) { 20 | const auto result = parse("withANumber(1)"); 21 | const auto fncall = child_nth<0, nfunction_call>(result); 22 | const auto left = left_op(fncall); 23 | const auto param = argument_nth<0, nconstant>(fncall); 24 | 25 | ASSERT_EQ(left->name, "withANumber"); 26 | ASSERT_EQ(fncall->arguments.size(), 1); 27 | ASSERT_EQ(param->type, nconstant_type::INTEGER); 28 | ASSERT_EQ(get(param->content).representation, "1"); 29 | } 30 | 31 | TEST(parser_function_call, reads_a_function_call_with_multiple_parameters) { 32 | const auto result = parse("withNumbers(1, 2.5)"); 33 | const auto fncall = child_nth<0, nfunction_call>(result); 34 | const auto left = left_op(fncall); 35 | const auto param1 = argument_nth<0, nconstant>(fncall); 36 | const auto param2 = argument_nth<1, nconstant>(fncall); 37 | 38 | ASSERT_EQ(left->name, "withNumbers"); 39 | ASSERT_EQ(fncall->arguments.size(), 2); 40 | ASSERT_EQ(param1->type, nconstant_type::INTEGER); 41 | ASSERT_EQ(get(param1->content).representation, "1"); 42 | ASSERT_EQ(param2->type, nconstant_type::FLOATING); 43 | ASSERT_EQ(get(param2->content).representation, "2.5"); 44 | } 45 | 46 | TEST(parser_function_call, reads_a_function_call_with_a_named_argument) { 47 | const auto result = parse("withNamedArgument(a=1)"); 48 | const auto fncall = child_nth<0, nfunction_call>(result); 49 | const auto left = left_op(fncall); 50 | const auto param1 = argument_nth<0, nfunction_call_named_argument>(fncall); 51 | const auto param1_value = argument_value(param1); 52 | 53 | ASSERT_EQ(left->name, "withNamedArgument"); 54 | ASSERT_EQ(fncall->arguments.size(), 1); 55 | ASSERT_EQ(param1->name, "a"); 56 | ASSERT_EQ(param1_value->type, nconstant_type::INTEGER); 57 | ASSERT_EQ(get(param1_value->content).representation, "1"); 58 | } 59 | 60 | TEST(parser_function_call, reads_a_function_call_with_multiple_named_arguments) { 61 | const auto result = parse("withMultipleNamedArguments(a=1, b='hey')"); 62 | const auto fncall = child_nth<0, nfunction_call>(result); 63 | const auto left = left_op(fncall); 64 | const auto param1 = argument_nth<0, nfunction_call_named_argument>(fncall); 65 | const auto param1_value = argument_value(param1); 66 | const auto param2 = argument_nth<1, nfunction_call_named_argument>(fncall); 67 | const auto param2_value = argument_value(param2); 68 | 69 | ASSERT_EQ(left->name, "withMultipleNamedArguments"); 70 | ASSERT_EQ(fncall->arguments.size(), 2); 71 | ASSERT_EQ(param1->name, "a"); 72 | ASSERT_EQ(param1_value->type, nconstant_type::INTEGER); 73 | ASSERT_EQ(get(param1_value->content).representation, "1"); 74 | ASSERT_EQ(param2->name, "b"); 75 | ASSERT_EQ(param2_value->type, nconstant_type::STRING); 76 | ASSERT_EQ(get(param2_value->content).content, "hey"); 77 | } -------------------------------------------------------------------------------- /diagnostic/src/diagnostic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace scc::diagnostic { 10 | using std::map; 11 | using std::list; 12 | using std::string; 13 | using std::shared_ptr; 14 | using std::initializer_list; 15 | using std::tm; 16 | 17 | enum class diagnostic_phase_id : unsigned char { 18 | DISCOVERY, 19 | LEXER, 20 | PARSER, 21 | PASS_DETECT_CLASSES, 22 | PASS_INTERNAL_MODELER, 23 | PASS_VALUE_CLASS_IR_TRANSFORMER, 24 | PASS_ENTITY_CLASS_IR_TRANSFORMER, 25 | PASS_ENTITY_METHOD_RESOLUTION, 26 | GENERATE_LLVM_IR, 27 | OPTIMIZE_LLVM_IR, 28 | EMIT_LLVM 29 | }; 30 | 31 | typedef unsigned int count_t; 32 | 33 | enum class diagnostic_log_level : unsigned char { 34 | DEBUG, INFO, WARN, ERROR, FATAL 35 | }; 36 | 37 | struct diagnostic_log_marker { 38 | string key; 39 | string value; 40 | }; 41 | 42 | struct diagnostic_log { 43 | diagnostic_log_level level; 44 | unsigned long when; 45 | 46 | string message; 47 | list markers; 48 | }; 49 | 50 | struct diagnostic_phase { 51 | diagnostic_phase_id id; 52 | unsigned long start; 53 | unsigned long end; 54 | 55 | count_t errors; 56 | count_t warnings; 57 | 58 | list> logs; 59 | }; 60 | 61 | struct diagnostic { 62 | diagnostic_log_level allowed_level; 63 | string scc_version; 64 | string os_version; 65 | 66 | unsigned long start; 67 | unsigned long end; 68 | 69 | count_t total_errors; 70 | count_t total_warnings; 71 | 72 | list> phases; 73 | }; 74 | 75 | void initialize(const diagnostic_log_level allowed_level); 76 | void start_phase(const diagnostic_phase_id phase); 77 | void end_phase(); 78 | void log(const diagnostic_log_level level, const string &format, const initializer_list markers); 79 | void finish(); 80 | 81 | void dump_diagnostic(const string &where); 82 | void print_user_diagnostic(); 83 | int return_code(); 84 | bool debug(); 85 | } 86 | 87 | inline void D_START_PHASE(scc::diagnostic::diagnostic_phase_id phase) { 88 | scc::diagnostic::start_phase(phase); 89 | } 90 | 91 | inline void D_END_PHASE() { 92 | scc::diagnostic::end_phase(); 93 | } 94 | 95 | inline void D_DEBUG(const std::string &fmt, const std::initializer_list markers) { 96 | scc::diagnostic::log(scc::diagnostic::diagnostic_log_level::DEBUG, fmt, markers); 97 | } 98 | 99 | inline void D_INFO(const std::string &fmt, const std::initializer_list markers) { 100 | scc::diagnostic::log(scc::diagnostic::diagnostic_log_level::INFO, fmt, markers); 101 | } 102 | 103 | inline void D_WARN(const std::string &fmt, const std::initializer_list markers) { 104 | scc::diagnostic::log(scc::diagnostic::diagnostic_log_level::WARN, fmt, markers); 105 | } 106 | 107 | inline void D_ERROR(const std::string &fmt, const std::initializer_list markers) { 108 | scc::diagnostic::log(scc::diagnostic::diagnostic_log_level::ERROR, fmt, markers); 109 | } 110 | 111 | inline void D_FATAL(const std::string &fmt, const std::initializer_list markers) { 112 | scc::diagnostic::log(scc::diagnostic::diagnostic_log_level::FATAL, fmt, markers); 113 | } 114 | 115 | inline bool D_DEBUGGING() { 116 | return scc::diagnostic::debug(); 117 | } -------------------------------------------------------------------------------- /backend-llvm/src/ir_builder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "llvm/ADT/APFloat.h" 4 | #include "llvm/ADT/Optional.h" 5 | #include "llvm/ADT/STLExtras.h" 6 | #include "llvm/IR/BasicBlock.h" 7 | #include "llvm/IR/Constants.h" 8 | #include "llvm/IR/DerivedTypes.h" 9 | #include "llvm/IR/Function.h" 10 | #include "llvm/IR/IRBuilder.h" 11 | #include "llvm/IR/Instructions.h" 12 | #include "llvm/IR/LLVMContext.h" 13 | #include "llvm/IR/LegacyPassManager.h" 14 | #include "llvm/IR/Module.h" 15 | #include "llvm/IR/Type.h" 16 | #include "llvm/IR/Verifier.h" 17 | #include "llvm/Support/TargetRegistry.h" 18 | #include "llvm/Support/FileSystem.h" 19 | #include "llvm/Support/Host.h" 20 | #include "llvm/Support/TargetSelect.h" 21 | #include "llvm/Support/raw_ostream.h" 22 | #include "llvm/Target/TargetMachine.h" 23 | #include "llvm/Target/TargetOptions.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "ast.h" 38 | #include "type_registry.h" 39 | #include "runtime/runtime.h" 40 | 41 | namespace scc::backend::llvm { 42 | using namespace ::llvm; 43 | using namespace scc::ast; 44 | using ::llvm::legacy::FunctionPassManager; 45 | using std::shared_ptr; 46 | using scc::ast::node_ref; 47 | using scc::type_system::type_registry; 48 | using std::shared_ptr; 49 | using std::make_shared; 50 | using std::map; 51 | using std::string; 52 | using std::optional; 53 | using std::shared_ptr; 54 | 55 | 56 | class ir_builder { 57 | public: 58 | ir_builder( 59 | shared_ptr &context, 60 | shared_ptr> &ir, 61 | shared_ptr &module, 62 | shared_ptr &pass_manager, 63 | shared_ptr &sonata_types 64 | ); 65 | 66 | ~ir_builder() = default; 67 | void build_ir(const scc::ast::ast_root &document); 68 | private: // IR generation methods 69 | Value *to_value(const node_ref &expr); 70 | Value *to_value(const expression_ref &expr); 71 | Value *to_value(const shared_ptr &expr); 72 | Value *to_value(const shared_ptr &expr); 73 | Value *to_value(const shared_ptr &expr); 74 | Value *to_value(const shared_ptr &expr); 75 | Value *to_value(const shared_ptr &expr); 76 | Value *to_value(const shared_ptr &expr); 77 | Value *to_value(const shared_ptr &expr); 78 | Value *to_value(const shared_ptr &expr); 79 | Value *to_value(const shared_ptr &expr); 80 | Value *to_value(const shared_ptr &expr); 81 | Value *to_value(const ast_block &expr); 82 | 83 | void register_extern_function(const shared_ptr &letfn); 84 | void register_function(const shared_ptr &letfn); 85 | void register_function(const shared_ptr &letfn); 86 | void register_struct(const shared_ptr &nstruct); 87 | private: // private state 88 | 89 | shared_ptr _context; 90 | shared_ptr> _builder; 91 | shared_ptr _module; 92 | shared_ptr _pass_manager; 93 | map _locals; 94 | map _params; 95 | map _strings; 96 | map _types; 97 | shared_ptr _sonata_types; 98 | runtime::runtime _rt; 99 | }; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /backend-llvm/src/backend.cpp: -------------------------------------------------------------------------------- 1 | #include "backend.h" 2 | 3 | #include "llvm/ADT/Optional.h" 4 | #include "llvm/IR/IRBuilder.h" 5 | #include "llvm/IR/LegacyPassManager.h" 6 | #include "llvm/Support/TargetRegistry.h" 7 | #include "llvm/Support/FileSystem.h" 8 | #include "llvm/Support/Host.h" 9 | #include "llvm/Support/TargetSelect.h" 10 | #include "llvm/Support/raw_ostream.h" 11 | #include "llvm/Target/TargetOptions.h" 12 | #include "llvm/Transforms/InstCombine/InstCombine.h" 13 | #include "llvm/Transforms/Scalar.h" 14 | #include "llvm/Transforms/Scalar/GVN.h" 15 | #include 16 | #include 17 | #include 18 | 19 | #include "intrinsics/intrinsics.h" 20 | #include "type_registry.h" 21 | #include "diagnostic.h" 22 | 23 | using namespace llvm; 24 | using namespace llvm::sys; 25 | 26 | namespace scc::backend::llvm { 27 | using scc::type_system::type_registry; 28 | using namespace intrinsics; 29 | 30 | llvm_backend::llvm_backend(std::shared_ptr &sonata_types) { 31 | _context = std::make_shared(); 32 | _module = std::make_shared("Sonata", *_context); 33 | _builder = std::make_shared>(*_context); 34 | _pass_manager = std::make_shared(_module.get()); 35 | _ir_builder = std::make_shared( 36 | _context, 37 | _builder, 38 | _module, 39 | _pass_manager, 40 | sonata_types 41 | ); 42 | 43 | InitializeAllTargetInfos(); 44 | InitializeAllTargets(); 45 | InitializeAllTargetMCs(); 46 | InitializeAllAsmParsers(); 47 | InitializeAllAsmPrinters(); 48 | 49 | _pass_manager->add(createAlignmentFromAssumptionsPass()); 50 | _pass_manager->add(createInstructionCombiningPass()); 51 | _pass_manager->add(createDeadCodeEliminationPass()); 52 | _pass_manager->add(createDeadStoreEliminationPass()); 53 | _pass_manager->add(createLoopUnrollPass()); 54 | _pass_manager->add(createFlattenCFGPass()); 55 | _pass_manager->add(createReassociatePass()); 56 | _pass_manager->add(createGVNPass()); 57 | _pass_manager->add(createTailCallEliminationPass()); 58 | _pass_manager->add(createCFGSimplificationPass()); 59 | 60 | _pass_manager->doInitialization(); 61 | 62 | auto TargetTriple = sys::getDefaultTargetTriple(); 63 | _module->setTargetTriple(TargetTriple); 64 | 65 | std::string Error; 66 | auto Target = TargetRegistry::lookupTarget(TargetTriple, Error); 67 | 68 | if (!Target) { 69 | errs() << Error; 70 | throw std::exception(); 71 | } 72 | 73 | auto CPU = "x86-64-v4"; 74 | auto Features = ""; 75 | 76 | TargetOptions opt; 77 | auto RM = Optional(); 78 | _target_machine = std::shared_ptr(Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM)); 79 | _module->setDataLayout(_target_machine->createDataLayout()); 80 | } 81 | 82 | void llvm_backend::write(const ast_root &document) { 83 | _ir_builder->build_ir(document); 84 | 85 | auto Filename = "output.o"; 86 | std::error_code EC; 87 | raw_fd_ostream dest(Filename, EC, sys::fs::OF_None); 88 | 89 | if (EC) { 90 | errs() << "Could not open file: " << EC.message(); 91 | throw std::exception(); 92 | } 93 | 94 | D_START_PHASE(scc::diagnostic::diagnostic_phase_id::OPTIMIZE_LLVM_IR); 95 | legacy::PassManager pass; 96 | auto FileType = CGFT_ObjectFile; 97 | 98 | if (_target_machine->addPassesToEmitFile(pass, dest, nullptr, FileType)) { 99 | errs() << "TheTargetMachine can't emit a file of this type"; 100 | throw std::exception(); 101 | } 102 | 103 | pass.run(*_module); 104 | dest.flush(); 105 | D_END_PHASE(); 106 | 107 | if (D_DEBUGGING()) { 108 | _module->print(errs(), nullptr); 109 | } 110 | 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /type-system/src/memory/internal_modeler.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "internal_modeler.h" 4 | 5 | using namespace scc::type_system::memory; 6 | using namespace scc::type_system; 7 | 8 | TEST(internal_modeler, merges_two_boolean_fields_into_a_bitbag_with_padding) { 9 | auto registry = std::make_shared(); 10 | internal_modeler modeler(64, registry); 11 | 12 | auto entity = registry->resolve("example_entity"); 13 | auto boolean = registry->resolve("boolean"); 14 | 15 | entity->kind = type_kind::ENTITY; 16 | 17 | entity->fields.emplace_back(std::make_shared(field { .base_type = boolean, .name = "f1" })); 18 | entity->fields.emplace_back(std::make_shared(field { .base_type = boolean, .name = "f2" })); 19 | 20 | modeler.model_type(entity); 21 | 22 | auto bb = std::get(entity->layout.storages[0]); 23 | ASSERT_EQ(entity->layout.type, layout_type::STATIC); 24 | 25 | ASSERT_EQ(bb.size, 8); 26 | ASSERT_EQ(bb.reservations[0].bits, 1); 27 | ASSERT_EQ(bb.reservations[0].type, bit_bag_reservation_type::BOOLEAN); 28 | ASSERT_EQ(bb.reservations[1].bits, 1); 29 | ASSERT_EQ(bb.reservations[1].type, bit_bag_reservation_type::BOOLEAN); 30 | ASSERT_EQ(entity->fields[0]->selector.type, selector_type::BIT_BAG); 31 | ASSERT_EQ(entity->fields[0]->selector.offset, 0); 32 | ASSERT_EQ(entity->fields[1]->selector.type, selector_type::BIT_BAG); 33 | ASSERT_EQ(entity->fields[1]->selector.offset, 1); 34 | } 35 | 36 | //TEST(internal_modeler, merges_value_classes_with_booleans_into_parent) { 37 | // auto registry = std::make_shared(); 38 | // internal_modeler modeler(64, registry); 39 | // 40 | // auto entity = registry->resolve("example"); 41 | // auto vc = registry->resolve("valueclass"); 42 | // auto boolean = registry->resolve("boolean"); 43 | // 44 | // entity->kind = type_kind::ENTITY; 45 | // vc->kind = type_kind::VALUE; 46 | // vc->fields.emplace_back(std::make_shared(field { .base_type = boolean, .name = "field" })); 47 | // entity->fields.emplace_back(std::make_shared(field { .base_type = vc, .name = "vc1" })); 48 | // entity->fields.emplace_back(std::make_shared(field { .base_type = vc, .name = "vc2" })); 49 | // modeler.model_type(entity); 50 | // 51 | // ASSERT_EQ(entity->layout.type, layout_type::STATIC); 52 | // 53 | // auto bb = std::get(entity->layout.storages[0]); 54 | // ASSERT_EQ(bb.size, 8); 55 | // ASSERT_EQ(bb.reservations[0].bits, 1); 56 | // ASSERT_EQ(bb.reservations[0].type, bit_bag_reservation_type::BOOLEAN); 57 | // ASSERT_EQ(bb.reservations[1].bits, 1); 58 | // ASSERT_EQ(bb.reservations[1].type, bit_bag_reservation_type::BOOLEAN); 59 | // ASSERT_EQ(entity->fields[0]->selector.type, selector_type::BIT_BAG); 60 | // ASSERT_EQ(entity->fields[0]->selector.offset, 0); 61 | // ASSERT_EQ(entity->fields[1]->selector.type, selector_type::BIT_BAG); 62 | // ASSERT_EQ(entity->fields[1]->selector.offset, 1); 63 | //} 64 | 65 | TEST(internal_modeler, merges_integers_and_bits) { 66 | auto registry = std::make_shared(); 67 | internal_modeler modeler(64, registry); 68 | 69 | auto entity = registry->resolve("example"); 70 | auto vc = registry->resolve("valueclass"); 71 | auto boolean = registry->resolve("boolean"); 72 | auto integer = registry->resolve("integer"); 73 | 74 | entity->kind = type_kind::ENTITY; 75 | vc->kind = type_kind::VALUE; 76 | vc->fields.emplace_back(std::make_shared(field { .base_type = boolean, .name = "field" })); 77 | entity->fields.emplace_back(std::make_shared(field { .base_type = vc, .name = "vc" })); 78 | entity->fields.emplace_back(std::make_shared(field { .base_type = integer, .name = "int" })); 79 | modeler.model_type(entity); 80 | 81 | ASSERT_EQ(entity->layout.type, layout_type::STATIC); 82 | 83 | auto direct = std::get(entity->layout.storages[0]); 84 | auto bb = std::get(entity->layout.storages[1]); 85 | auto pad = std::get(entity->layout.storages[2]); 86 | ASSERT_EQ(bb.size, 8); 87 | ASSERT_EQ(bb.reservations[0].bits, 1); 88 | ASSERT_EQ(bb.reservations[0].type, bit_bag_reservation_type::BOOLEAN); 89 | ASSERT_EQ(direct.size, 32); 90 | ASSERT_EQ(pad.size, 64 - 32 - 8); 91 | } -------------------------------------------------------------------------------- /backend-llvm/src/runtime/runtime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "llvm/IR/LLVMContext.h" 7 | #include "llvm/ADT/APFloat.h" 8 | #include "llvm/ADT/Optional.h" 9 | #include "llvm/ADT/STLExtras.h" 10 | #include "llvm/IR/BasicBlock.h" 11 | #include "llvm/IR/Constants.h" 12 | #include "llvm/IR/DerivedTypes.h" 13 | #include "llvm/IR/Function.h" 14 | #include "llvm/IR/IRBuilder.h" 15 | #include "llvm/IR/Instructions.h" 16 | #include "llvm/IR/LLVMContext.h" 17 | #include "llvm/IR/LegacyPassManager.h" 18 | #include "llvm/IR/Module.h" 19 | #include "llvm/IR/Type.h" 20 | #include "llvm/IR/Verifier.h" 21 | #include "llvm/Support/TargetRegistry.h" 22 | #include "llvm/Support/FileSystem.h" 23 | #include "llvm/Support/Host.h" 24 | #include "llvm/Support/TargetSelect.h" 25 | #include "llvm/Support/raw_ostream.h" 26 | #include "llvm/Target/TargetMachine.h" 27 | #include "llvm/Target/TargetOptions.h" 28 | 29 | namespace scc::backend::llvm::runtime { 30 | using namespace ::llvm; 31 | using namespace ::llvm; 32 | using namespace std; 33 | 34 | class runtime { 35 | public: 36 | explicit runtime(shared_ptr ctx, shared_ptr mod): _context(std::move(ctx)), _module(std::move(mod)) {}; 37 | ~runtime() = default; 38 | 39 | inline Value *mkactorsystem(shared_ptr> &builder, Value *value) { 40 | auto fn = _module->getOrInsertFunction("mkactorsystem", FunctionType::get(Type::getVoidTy(*_context), { Type::getInt8Ty(*_context) }, false)); 41 | return builder->CreateCall(fn, value); 42 | } 43 | 44 | inline Value *dlactorsystem(shared_ptr> &builder) { 45 | auto fn = _module->getOrInsertFunction("dlactorsystem", FunctionType::get(Type::getVoidTy(*_context), { Type::getInt8Ty(*_context) }, false)); 46 | return builder->CreateCall(fn); 47 | } 48 | 49 | inline Value *mkaddress(shared_ptr> &builder) { 50 | auto fn = _module->getOrInsertFunction("mkaddress", FunctionType::get(Type::getInt64Ty(*_context), {}, false)); 51 | return builder->CreateCall(fn); 52 | } 53 | 54 | inline Value *getactorsystem(shared_ptr> &builder) { 55 | auto fn = _module->getOrInsertFunction("getactorsystem", FunctionType::get(Type::getInt32PtrTy(*_context), {}, false)); 56 | return builder->CreateCall(fn); 57 | } 58 | 59 | inline Value *getmailbox(shared_ptr> &builder) { 60 | auto fn = _module->getOrInsertFunction("getmailbox", FunctionType::get(Type::getInt32PtrTy(*_context), {}, false)); 61 | return builder->CreateCall(fn); 62 | } 63 | 64 | inline Value *mkactor(shared_ptr> &builder, Value *address, Value *supervisor, Value *initial, Value *mb, Value *type) { 65 | auto fn = _module->getOrInsertFunction( 66 | "mkactor", 67 | FunctionType::get(Type::getInt32PtrTy(*_context), { 68 | Type::getInt64Ty(*_context), // actor address 69 | Type::getInt64Ty(*_context), // actor supervisor address 70 | Type::getInt32PtrTy(*_context), // initial state pointer 71 | Type::getInt32PtrTy(*_context), // mailbox pointer 72 | Type::getInt32PtrTy(*_context), // type pointer 73 | }, false) 74 | ); 75 | 76 | return builder->CreateCall(fn, { 77 | address, 78 | supervisor, 79 | initial, 80 | mb, 81 | type, 82 | }); 83 | } 84 | 85 | inline Value *dlactor(shared_ptr> &builder, Value *actor) { 86 | auto fn = _module->getOrInsertFunction("dlactor", FunctionType::get(Type::getInt32Ty(*_context), { Type::getInt32Ty(*_context) }, false)); 87 | return builder->CreateCall(fn, actor); 88 | } 89 | 90 | inline Value *extern_malloc(shared_ptr> &builder, uint32_t size) { 91 | auto fn = _module->getOrInsertFunction("malloc", FunctionType::get(Type::getInt32Ty(*_context), { Type::getInt32Ty(*_context) }, false)); 92 | return builder->CreateCall(fn, { ConstantInt::get(*_context, APInt(32, size )) }); 93 | } 94 | 95 | private: 96 | shared_ptr _context; 97 | shared_ptr _module; 98 | }; 99 | 100 | } // runtime 101 | -------------------------------------------------------------------------------- /runtime/vm/src/api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "concurrency/spin_lock.h" 4 | #include "mailbox/fiber.h" 5 | #include "api.h" 6 | 7 | namespace vmc_ = vm::concurrency; 8 | 9 | vma::actor_system *CURRENT_ACTOR_SYSTEM = nullptr; 10 | vmm::mailbox** CURRENT_MAILBOXES = nullptr; 11 | vmm::fiber** CURRENT_FIBERS = nullptr; 12 | thread_count_t CURRENT_FIBERS_COUNT = 0; 13 | thread_count_t CURRENT_MAILBOXES_COUNT = 0; 14 | thread_count_t CURRENT_MAILBOX = 0; 15 | vmc_::spin_lock MAILBOX_LIST_LOCK; 16 | 17 | vmc::address mkaddress() { 18 | return vmc::make_address(); 19 | } 20 | 21 | vma::actor *mkactor(vmc::address addr, vmc::address supervisor, vma::base_actor_state *state, vmm::mailbox *mb, vma::actor_type *type) { 22 | return new vma::actor(addr, supervisor, std::unique_ptr(state), std::shared_ptr(mb), std::shared_ptr(type), std::shared_ptr(CURRENT_ACTOR_SYSTEM)); 23 | } 24 | 25 | void dlactor(vma::actor *actor) { 26 | delete actor; 27 | } 28 | 29 | void actor_receive(vma::actor *rcv, vmm::message *msg) { 30 | rcv->push_from(std::unique_ptr(msg)); 31 | } 32 | 33 | void actor_send(vma::actor *actor, vmm::message *msg, vmc::address &rcv) { 34 | actor->send(std::unique_ptr(msg), rcv); 35 | } 36 | 37 | void *actor_state(vma::actor *actor) { 38 | auto state = actor->state_as(); 39 | return static_cast(state.get()); 40 | } 41 | 42 | vma::actor_system *mkactorsystem(thread_count_t count) { 43 | auto actor_system = new vma::actor_system(); 44 | 45 | if (count == 0) { 46 | count = std::thread::hardware_concurrency(); 47 | if (count == 0) { 48 | count = 1; 49 | } else { 50 | count *= 2; 51 | } 52 | } 53 | 54 | CURRENT_FIBERS_COUNT = count; 55 | CURRENT_MAILBOXES_COUNT = count; 56 | CURRENT_FIBERS = new vmm::fiber*[count]; 57 | CURRENT_MAILBOXES = new vmm::mailbox*[count]; 58 | CURRENT_MAILBOX = 0; 59 | 60 | auto using_system = [actor_system](vm::actor::address addr) -> std::shared_ptr { 61 | return actor_system->resolve_by_address(addr); 62 | }; 63 | 64 | for (auto i = 0; i < count; i++) { 65 | CURRENT_MAILBOXES[i] = new vmm::mailbox(); 66 | CURRENT_FIBERS[i] = new vmm::fiber(std::shared_ptr(CURRENT_MAILBOXES[i]), using_system); 67 | } 68 | 69 | CURRENT_ACTOR_SYSTEM = actor_system; 70 | return CURRENT_ACTOR_SYSTEM; 71 | } 72 | 73 | vma::actor_system *getactorsystem() { 74 | return CURRENT_ACTOR_SYSTEM; 75 | } 76 | 77 | vmm::mailbox *getmailbox() { 78 | auto _lock = MAILBOX_LIST_LOCK.lock(); 79 | auto next_mailbox = (CURRENT_MAILBOX + 1) % CURRENT_MAILBOXES_COUNT; 80 | auto mb = CURRENT_MAILBOXES[CURRENT_MAILBOX]; 81 | CURRENT_MAILBOX = next_mailbox; 82 | return mb; 83 | } 84 | 85 | void dlactorsystem() { 86 | for (auto i = 0; i < CURRENT_FIBERS_COUNT; i++) { 87 | CURRENT_FIBERS[i]->stop(); 88 | delete CURRENT_FIBERS[i]; 89 | } 90 | 91 | delete CURRENT_FIBERS; 92 | delete CURRENT_ACTOR_SYSTEM; 93 | } 94 | 95 | vma::actor *actorsystem_resolve_by_address(const vmc::address &addr) { 96 | return CURRENT_ACTOR_SYSTEM->resolve_by_address(addr).get(); 97 | } 98 | 99 | void actorsystem_register_actor(vma::actor *actor) { 100 | CURRENT_ACTOR_SYSTEM->register_actor(std::shared_ptr(actor)); 101 | } 102 | 103 | vma::actor_type *mkactortype(const char *name) { 104 | return new vma::actor_type(std::string(name)); 105 | } 106 | 107 | void actortype_register(vma::actor_type *type, const char *msg, const vma::dispatch_message &dispatch) { 108 | type->dispatch_table[std::string(msg)] = dispatch; 109 | } 110 | 111 | vmm::message *mkmsg(vma::actor *sender, const char *name) { 112 | auto msg = new vmm::message(); 113 | msg->sender = sender->get_address(); 114 | msg->message = std::string(name); 115 | 116 | return msg; 117 | } 118 | 119 | void message_set_metadata(vmm::message *msg, const char *name, const char *value) { 120 | msg->metadata[std::string(name)] = std::string(value); 121 | } 122 | 123 | void message_push_arg(vmm::message *msg, const char *name) { 124 | msg->arguments.emplace_back(name); 125 | } 126 | 127 | void *message_pop_arg(vmm::message *msg) { 128 | auto arg = msg->arguments.back(); 129 | msg->arguments.pop_back(); 130 | 131 | return (void *) arg.c_str(); 132 | } 133 | 134 | void message_send(vma::actor *sender, vmm::message *msg, vma::address &address) { 135 | sender->send(std::unique_ptr(msg), address); 136 | } 137 | -------------------------------------------------------------------------------- /runtime/vm/src/actor/actor.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "actor_type.h" 6 | #include "actor_system.h" 7 | #include "actor.h" 8 | 9 | struct actor_state : public vm::actor::base_actor_state { 10 | bool called; 11 | }; 12 | 13 | TEST(actor, dispatches_messages) { 14 | auto system = std::make_shared(); 15 | auto msg = std::make_unique(); 16 | msg->message = "hello"; 17 | 18 | auto instance_type = std::make_shared(); 19 | instance_type->class_name = "Greeter"; 20 | instance_type->dispatch_table["hello"] = [](vm::actor::actor *i, std::unique_ptr msg) -> vm::actor::actor_message_process_result { 21 | auto ptr_state = std::reinterpret_pointer_cast(i->state_as()); 22 | ptr_state->called = true; 23 | return vm::actor::OK; 24 | }; 25 | 26 | auto instance = std::make_shared( 27 | vm::core::make_address(), 28 | vm::core::make_address(), 29 | std::make_unique(actor_state { .called = false }), 30 | std::shared_ptr(), 31 | instance_type, 32 | system 33 | ); 34 | 35 | instance->process_message(std::move(msg)); 36 | auto state = std::reinterpret_pointer_cast(instance->state_as()); 37 | ASSERT_EQ(state->called, true); 38 | } 39 | 40 | TEST(actor, sends_messages_to_other_actors) { 41 | auto system = std::make_shared(); 42 | auto mb = std::make_shared(); 43 | auto msg = std::make_unique(); 44 | msg->message = "hello"; 45 | 46 | auto instance_type = std::make_shared(); 47 | instance_type->class_name = "Greeter"; 48 | instance_type->dispatch_table["hello"] = [](vm::actor::actor *i, std::unique_ptr msg) -> vm::actor::actor_message_process_result { 49 | auto ptr_state = std::reinterpret_pointer_cast(i->state_as()); 50 | ptr_state->called = true; 51 | 52 | return vm::actor::OK; 53 | }; 54 | 55 | const vm::core::address &receiver_address = vm::core::make_address(); 56 | auto instance = std::make_shared( 57 | receiver_address, 58 | vm::core::make_address(), 59 | std::make_unique(actor_state { .called = false }), 60 | mb, 61 | instance_type, 62 | system 63 | ); 64 | 65 | auto sender_instance = std::make_shared( 66 | vm::core::make_address(), 67 | vm::core::make_address(), 68 | std::make_unique(actor_state { .called = false }), 69 | mb, 70 | instance_type, 71 | system 72 | ); 73 | 74 | system->register_actor(instance); 75 | system->register_actor(sender_instance); 76 | 77 | sender_instance->send(std::move(msg), receiver_address); 78 | auto msg_in_mb = mb->dequeue(); 79 | instance->process_message(std::move(msg_in_mb)); 80 | 81 | auto state = std::reinterpret_pointer_cast(instance->state_as()); 82 | ASSERT_EQ(state->called, true); 83 | } 84 | 85 | TEST(actor, that_a_trace_is_added) { 86 | auto system = std::make_shared(); 87 | auto mb = std::make_shared(); 88 | auto msg = std::make_unique(); 89 | msg->message = "hello"; 90 | 91 | const vm::core::address &receiver_address = vm::core::make_address(); 92 | const vm::core::address &sender_address = vm::core::make_address(); 93 | 94 | auto instance = std::make_shared( 95 | receiver_address, 96 | vm::core::make_address(), 97 | std::make_unique(actor_state { .called = false }), 98 | mb, 99 | nullptr, 100 | system 101 | ); 102 | 103 | auto sender = std::make_shared( 104 | sender_address, 105 | vm::core::make_address(), 106 | std::make_unique(actor_state { .called = false }), 107 | mb, 108 | nullptr, 109 | system 110 | ); 111 | 112 | system->register_actor(instance); 113 | sender->send(std::move(msg), receiver_address); 114 | 115 | auto msg_in_mb = mb->dequeue(); 116 | 117 | ASSERT_EQ(msg_in_mb->traces.size(), 1); 118 | 119 | auto trace = msg_in_mb->traces.front(); 120 | ASSERT_EQ(trace.message, "hello"); 121 | ASSERT_EQ(trace.stepped_actor.id, sender_address.id); 122 | } -------------------------------------------------------------------------------- /parser/src/parser_let.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-includes/includes.hpp" 3 | 4 | using namespace scc::parser::test; 5 | using namespace scc::ast; 6 | using namespace scc::common; 7 | 8 | using std::get; 9 | 10 | TEST(parser_let, reads_a_let_expression_with_a_value) { 11 | const auto result = parse("let x = 1"); 12 | const auto let = child_nth<0, nlet>(result); 13 | const auto body = body_of(let); 14 | 15 | ASSERT_EQ(let->mutable_p, false); 16 | ASSERT_EQ(let->name, "x"); 17 | ASSERT_EQ(body->type, nconstant_type::INTEGER); 18 | ASSERT_EQ(get(body->content).base, numeric_base::DECIMAL); 19 | ASSERT_EQ(get(body->content).representation, "1"); 20 | } 21 | 22 | TEST(parser_let, reads_a_mutable_let_expression_with_a_value) { 23 | const auto result = parse("let mutable x = 0x15"); 24 | const auto let = child_nth<0, nlet>(result); 25 | const auto body = body_of(let); 26 | 27 | ASSERT_EQ(let->mutable_p, true); 28 | ASSERT_EQ(let->name, "x"); 29 | ASSERT_EQ(body->type, nconstant_type::INTEGER); 30 | ASSERT_EQ(get(body->content).base, numeric_base::HEXADECIMAL); 31 | ASSERT_EQ(get(body->content).representation, "0x15"); 32 | } 33 | 34 | TEST(parser_let, reads_a_let_expression_with_a_type_expression) { 35 | const auto result = parse("let withTypeExpr: integer = 0o6"); 36 | const auto let = child_nth<0, nlet>(result); 37 | const auto body = body_of(let); 38 | 39 | ASSERT_EQ(let->mutable_p, false); 40 | ASSERT_EQ(let->name, "withTypeExpr"); 41 | ASSERT_EQ(body->type, nconstant_type::INTEGER); 42 | ASSERT_EQ(get(let->constraints).type, "integer"); 43 | ASSERT_EQ(get(body->content).base, numeric_base::OCTAL); 44 | ASSERT_EQ(get(body->content).representation, "0o6"); 45 | } 46 | 47 | TEST(parser_let, reads_a_mutable_let_expression_without_body) { 48 | const auto result = parse("let mutable y: integer"); 49 | const auto let = child_nth<0, nlet>(result); 50 | 51 | ASSERT_EQ(let->mutable_p, true); 52 | ASSERT_EQ(let->name, "y"); 53 | ASSERT_FALSE(has_body(let)); 54 | } 55 | 56 | TEST(parser_let, reads_a_function_expression_without_parameters_and_without_body) { 57 | const auto result = parse("let foo(): number"); 58 | const auto let = child_nth<0, nlet_function>(result); 59 | 60 | ASSERT_EQ(let->name, "foo"); 61 | ASSERT_EQ(get(let->return_type).type, "number"); 62 | ASSERT_FALSE(has_body(let)); 63 | ASSERT_FALSE(has_parameters(let)); 64 | } 65 | 66 | TEST(parser_let, reads_a_function_expression_with_a_named_parameter_and_without_body) { 67 | const auto result = parse("let foo(param: string): number"); 68 | const auto let = child_nth<0, nlet_function>(result); 69 | 70 | ASSERT_EQ(let->name, "foo"); 71 | ASSERT_EQ(get(let->return_type).type, "number"); 72 | ASSERT_FALSE(has_body(let)); 73 | ASSERT_TRUE(has_parameters(let)); 74 | 75 | auto param = parameter_nth<0, nlet_function_named_parameter>(let); 76 | ASSERT_EQ(get(param->type).type, "string"); 77 | ASSERT_EQ(param->name, "param"); 78 | } 79 | 80 | TEST(parser_let, reads_an_external_function_expression) { 81 | const auto result = parse("let extern printf(format: string): none"); 82 | const auto let = child_nth<0, nlet_function>(result); 83 | 84 | ASSERT_EQ(let->name, "printf"); 85 | ASSERT_EQ(get(let->return_type).type, "none"); 86 | ASSERT_FALSE(has_body(let)); 87 | ASSERT_TRUE(has_parameters(let)); 88 | ASSERT_TRUE(let->external); 89 | 90 | auto param = parameter_nth<0, nlet_function_named_parameter>(let); 91 | ASSERT_EQ(get(param->type).type, "string"); 92 | ASSERT_EQ(param->name, "format"); 93 | } 94 | 95 | TEST(parser_let, reads_an_external_function_with_mapping_expression) { 96 | const auto result = parse("let extern printf(format: mapping[string, c_string]): none"); 97 | const auto let = child_nth<0, nlet_function>(result); 98 | 99 | ASSERT_EQ(let->name, "printf"); 100 | ASSERT_EQ(get(let->return_type).type, "none"); 101 | ASSERT_FALSE(has_body(let)); 102 | ASSERT_TRUE(has_parameters(let)); 103 | ASSERT_TRUE(let->external); 104 | 105 | auto param = parameter_nth<0, nlet_function_named_parameter>(let); 106 | ASSERT_EQ(param->name, "format"); 107 | auto gentype = get(param->type); 108 | ASSERT_EQ(gentype.base, "mapping"); 109 | auto p1 = generic_parameter_nth<0, type_constraint_equality>(gentype); 110 | ASSERT_EQ(p1.type, "string"); 111 | auto p2 = generic_parameter_nth<1, type_constraint_equality>(gentype); 112 | ASSERT_EQ(p2.type, "c_string"); 113 | } -------------------------------------------------------------------------------- /documentation/advanced/memory-model/img/0-memory-model-boundaries-ABI.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | Accountname: immutable stringbalance: mutable doublequeryAccountStatecommandsMemory Model BoundariesABIABI -------------------------------------------------------------------------------- /parser/src/parser_class.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-includes/includes.hpp" 3 | 4 | using namespace scc::parser::test; 5 | using namespace scc::ast; 6 | using namespace scc::common; 7 | 8 | TEST(parser_class, reads_a_capability_class_without_fields) { 9 | const auto result = parse("capability class IO()"); 10 | const auto klass = child_nth<0, nclass>(result); 11 | 12 | ASSERT_EQ(klass->type, nclass_type::CAPABILITY); 13 | ASSERT_EQ(klass->name, "IO"); 14 | ASSERT_EQ(klass->package, ""); 15 | ASSERT_EQ(klass->visibility, nclass_visibility::PUBLIC); 16 | ASSERT_TRUE(klass->requirements.empty()); 17 | ASSERT_TRUE(klass->fields.empty()); 18 | ASSERT_FALSE(klass->body.has_value()); 19 | } 20 | 21 | TEST(parser_class, reads_a_capability_class_with_a_field) { 22 | const auto result = parse("capability class IO(path: String)"); 23 | const auto klass = child_nth<0, nclass>(result); 24 | 25 | ASSERT_EQ(klass->type, nclass_type::CAPABILITY); 26 | ASSERT_EQ(klass->name, "IO"); 27 | ASSERT_EQ(klass->package, ""); 28 | ASSERT_EQ(klass->visibility, nclass_visibility::PUBLIC); 29 | ASSERT_TRUE(klass->requirements.empty()); 30 | ASSERT_FALSE(klass->fields.empty()); 31 | ASSERT_FALSE(klass->body.has_value()); 32 | 33 | auto path_field = field_nth<0>(klass); 34 | ASSERT_EQ(path_field->name, "path"); 35 | ASSERT_EQ(get(path_field->type).type, "String"); 36 | } 37 | 38 | TEST(parser_class, reads_a_capability_class_with_multiple_field) { 39 | const auto result = parse("capability class IO(path: String, permissions: Number)"); 40 | const auto klass = child_nth<0, nclass>(result); 41 | 42 | ASSERT_EQ(klass->type, nclass_type::CAPABILITY); 43 | ASSERT_EQ(klass->name, "IO"); 44 | ASSERT_EQ(klass->package, ""); 45 | ASSERT_EQ(klass->visibility, nclass_visibility::PUBLIC); 46 | ASSERT_TRUE(klass->requirements.empty()); 47 | ASSERT_FALSE(klass->fields.empty()); 48 | ASSERT_FALSE(klass->body.has_value()); 49 | 50 | auto path_field = field_nth<0>(klass); 51 | ASSERT_EQ(path_field->name, "path"); 52 | ASSERT_EQ(get(path_field->type).type, "String"); 53 | 54 | auto permissions_field = field_nth<1>(klass); 55 | ASSERT_EQ(permissions_field->name, "permissions"); 56 | ASSERT_EQ(get(permissions_field->type).type, "Number"); 57 | } 58 | 59 | TEST(parser_class, reads_a_value_class_with_multiple_field) { 60 | const auto result = parse("value class Amount(value: Number, currency: String)"); 61 | const auto klass = child_nth<0, nclass>(result); 62 | 63 | ASSERT_EQ(klass->type, nclass_type::VALUE); 64 | ASSERT_EQ(klass->name, "Amount"); 65 | ASSERT_EQ(klass->package, ""); 66 | ASSERT_EQ(klass->visibility, nclass_visibility::PUBLIC); 67 | ASSERT_TRUE(klass->requirements.empty()); 68 | ASSERT_FALSE(klass->fields.empty()); 69 | ASSERT_FALSE(klass->body.has_value()); 70 | 71 | auto path_field = field_nth<0>(klass); 72 | ASSERT_EQ(path_field->name, "value"); 73 | ASSERT_EQ(get(path_field->type).type, "Number"); 74 | 75 | auto permissions_field = field_nth<1>(klass); 76 | ASSERT_EQ(permissions_field->name, "currency"); 77 | ASSERT_EQ(get(permissions_field->type).type, "String"); 78 | } 79 | 80 | TEST(parser_class, reads_an_entity_interface_with_a_method) { 81 | const auto result = parse("entity interface Ping {\n" 82 | " let do(): forget\n" 83 | "}"); 84 | const auto klass = child_nth<0, nclass>(result); 85 | 86 | ASSERT_EQ(klass->type, nclass_type::ENTITY_INTERFACE); 87 | ASSERT_EQ(klass->name, "Ping"); 88 | ASSERT_EQ(klass->package, ""); 89 | ASSERT_EQ(klass->visibility, nclass_visibility::PUBLIC); 90 | ASSERT_TRUE(klass->requirements.empty()); 91 | ASSERT_TRUE(klass->fields.empty()); 92 | ASSERT_TRUE(klass->body.has_value()); 93 | 94 | auto fdef = child_nth<0, nlet_function>(klass); 95 | ASSERT_EQ(fdef->name, "do"); 96 | ASSERT_EQ(get(fdef->return_type).type, "forget"); 97 | } 98 | 99 | TEST(parser_class, reads_a_self_set_value) { 100 | const auto result = parse("self.field = 1"); 101 | const auto setter = child_nth<0, nclass_self_set>(result); 102 | 103 | ASSERT_EQ(setter->selector, std::list { "field" }); 104 | 105 | auto nconst = std::dynamic_pointer_cast(setter->value); 106 | ASSERT_EQ(nconst->type, nconstant_type::INTEGER); 107 | ASSERT_EQ(std::get(nconst->content).representation, "1"); 108 | } 109 | 110 | TEST(parser_class, reads_a_self_set_value_deep) { 111 | const auto result = parse("self.field1.field2 = 1"); 112 | const auto setter = child_nth<0, nclass_self_set>(result); 113 | 114 | auto list = std::list { "field1", "field2" }; 115 | ASSERT_EQ(setter->selector, list); 116 | 117 | auto nconst = std::dynamic_pointer_cast(setter->value); 118 | ASSERT_EQ(nconst->type, nconstant_type::INTEGER); 119 | ASSERT_EQ(std::get(nconst->content).representation, "1"); 120 | } 121 | 122 | 123 | TEST(parser_class, reads_a_get_value_deep) { 124 | const auto result = parse("self.field1.field2"); 125 | const auto getter = child_nth<0, nclass_self_get>(result); 126 | 127 | auto list = std::list { "field1", "field2" }; 128 | ASSERT_EQ(getter->selector, list); 129 | } 130 | 131 | 132 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | kevin.mas@hey.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /type-system/src/memory/internal_modeler.cpp: -------------------------------------------------------------------------------- 1 | #include "internal_modeler.h" 2 | 3 | #include 4 | 5 | const unsigned int BYTE_SIZE = 8; 6 | const unsigned int POINTER_SIZE = 64; 7 | 8 | namespace scc::type_system::memory { 9 | internal_modeler::internal_modeler(const unsigned int goal_cache_size, std::shared_ptr &types) 10 | : goal_cache_size(goal_cache_size), types(types) { 11 | boolean_type = types->resolve("boolean"); 12 | boolean_type->kind = type_kind::PRIMITIVE; 13 | boolean_type->layout = { .type = layout_type::STATIC, .storages = { { bit_bag { .size = 1, .reservations = { { .bits = 1, .type = bit_bag_reservation_type::BOOLEAN, .translations = { }}}}}}}; 14 | 15 | byte_type = types->resolve("byte"); 16 | byte_type->kind = type_kind::PRIMITIVE; 17 | byte_type->layout = { .type = layout_type::STATIC, .storages = { { direct_mapping { .size = BYTE_SIZE } } } }; 18 | 19 | short_type = types->resolve("short"); 20 | short_type->kind = type_kind::PRIMITIVE; 21 | short_type->layout = { .type = layout_type::STATIC, .storages = { { direct_mapping { .size = BYTE_SIZE * 2 } } } }; 22 | 23 | integer_type = types->resolve("integer"); 24 | integer_type->kind = type_kind::PRIMITIVE; 25 | integer_type->layout = { .type = layout_type::STATIC, .storages = { { direct_mapping { .size = BYTE_SIZE * 4 } } } }; 26 | 27 | long_type = types->resolve("long"); 28 | long_type->kind = type_kind::PRIMITIVE; 29 | long_type->layout = { .type = layout_type::STATIC, .storages = { { direct_mapping { .size = BYTE_SIZE * 8 } } } }; 30 | 31 | floating_type = types->resolve("float"); 32 | floating_type->kind = type_kind::PRIMITIVE; 33 | floating_type->layout = { .type = layout_type::STATIC, .storages = { { direct_mapping { .size = BYTE_SIZE * 4 } } } }; 34 | 35 | double_type = types->resolve("double"); 36 | double_type->kind = type_kind::PRIMITIVE; 37 | double_type->layout = { .type = layout_type::STATIC, .storages = { { direct_mapping { .size = BYTE_SIZE * 8 } } } }; 38 | 39 | } 40 | 41 | internal_modeler::~internal_modeler() = default; 42 | 43 | void internal_modeler::model_type(std::shared_ptr &type) const { 44 | type->layout.type = layout_type::STATIC; 45 | 46 | unsigned int offset = 0; 47 | auto current_bitbag = bit_bag { .size = BYTE_SIZE, .reservations = {} }; 48 | auto remaining_from_bitbag = BYTE_SIZE; 49 | 50 | for (auto &field : type->fields) { 51 | merge_into_parent(type, offset, current_bitbag, remaining_from_bitbag, field); 52 | } 53 | 54 | if (!current_bitbag.reservations.empty()) { 55 | type->layout.storages.emplace_back(current_bitbag); 56 | } 57 | 58 | pad_to_cacheable(type); 59 | } 60 | 61 | void internal_modeler::merge_into_parent(std::shared_ptr &root, unsigned int &offset, bit_bag ¤t_bitbag, unsigned int &remaining_from_bitbag, const std::shared_ptr &field) const { 62 | const auto field_type = field->base_type; 63 | 64 | if (field_type->kind == type_kind::PRIMITIVE) { 65 | // primitives can be easily merged because they are always static and have a fixed size 66 | if (field_type == boolean_type) { 67 | field->selector = selector { .type = selector_type::BIT_BAG, .offset = current_bitbag.size - remaining_from_bitbag }; 68 | current_bitbag.reservations.emplace_back(bit_bag_reservation { .bits = 1, .type = bit_bag_reservation_type::BOOLEAN }); 69 | 70 | remaining_from_bitbag--; 71 | } 72 | 73 | if (field_type == byte_type || field_type == short_type || field_type == integer_type || field_type == long_type || field_type == floating_type || field_type == double_type) { 74 | field->selector = selector { .type = selector_type::DIRECT, .offset = offset++ }; 75 | root->layout.storages.insert(root->layout.storages.end(), field_type->layout.storages.begin(),field_type->layout.storages.end()); 76 | } 77 | 78 | } else if (field_type->kind == type_kind::VALUE) { 79 | for (const auto& inner_field : field_type->fields) { 80 | merge_into_parent(root, offset, current_bitbag, remaining_from_bitbag, inner_field); 81 | } 82 | } 83 | } 84 | 85 | void internal_modeler::pad_to_cacheable(std::shared_ptr &root) const { 86 | unsigned int size = 0; 87 | 88 | for (auto &storage : root->layout.storages) { 89 | if (std::holds_alternative(storage)) { 90 | size += std::get(storage).size; 91 | } 92 | 93 | if (std::holds_alternative(storage)) { 94 | size += std::get(storage).size; 95 | } 96 | 97 | if (std::holds_alternative(storage)) { 98 | size += POINTER_SIZE; 99 | } 100 | 101 | if (std::holds_alternative(storage)) { 102 | size += std::get(storage).size; 103 | } 104 | } 105 | 106 | unsigned int pad = 0; 107 | if (size == 0) { 108 | return; 109 | } 110 | 111 | if (size > goal_cache_size) { 112 | pad += size % goal_cache_size; 113 | } else { 114 | if (size < goal_cache_size / 2) { 115 | pad += ((goal_cache_size / 2) % size); 116 | } else { 117 | pad += (goal_cache_size % size); 118 | } 119 | } 120 | 121 | if (pad > 0) { 122 | root->layout.storages.emplace_back(padding { .size = pad }); 123 | } 124 | 125 | size += pad; 126 | root->layout.size_in_bytes = size / 8; 127 | } 128 | 129 | void internal_modeler::model_all_types() const { 130 | for (auto &t : types->all_types()) { 131 | model_type(t); 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /documentation/advanced/memory-model/1-memory-model.md: -------------------------------------------------------------------------------- 1 | # Sonata's Memory Model 2 | 3 | Sonata's memory model is based on memory co-location and cache usage. Most of the time, Sonata 4 | will decide what is the best tradeoff on memory usage, however, hints can be applied at the code level 5 | to avoid some Sonata optimisations. 6 | 7 | To understand how Sonata defines the memory, let's start with the memory object model. 8 | 9 | ## Sonata's Memory Object Model 10 | 11 | Sonata defines memory models using two basic object types in a unified memory model. 12 | 13 | * Static Objects 14 | * Flexible Objects 15 | 16 | ### Static Objects 17 | 18 | Static objects have a fixed size and can be embedded or directly mapped to memory regions to optimise memory usage and 19 | access patterns. Example of static objects are numbers, enums, a booleans. 20 | 21 | Value classes can be static objects, if, and only if, they are formed by other static objects. Example static value class: 22 | 23 | ```sn 24 | value class Amount(value: double, currency: 'USD' | 'EUR') 25 | ``` 26 | 27 | ### Flexible Objects 28 | 29 | Flexible objects have an unbound size. This means that they can dynamically grow and shrink 30 | depending on the business logic of your application. 31 | 32 | Examples of flexible objects are strings, arrays, lists and trees. 33 | 34 | Value classes are flexible if at least on or their members is also flexible. 35 | 36 | ```sn 37 | value class Amount(value: double, currency: string) 38 | ``` 39 | 40 | ## Internal Modeling 41 | 42 | Internal modeling is the process of defining an optimized memory layout for a specific entity. Internal 43 | modeling is a complex process, and it's always refined for better optimisations. 44 | 45 | However, there are three principles in the internal modeling that are important: 46 | 47 | * First: optimize for cache usage 48 | * Second: optimize for memory footprint 49 | * Third: optimize for most common cases 50 | 51 | Internal modeling uses bit bags, direct mappings, references and paddings to implement the memory model. 52 | 53 | ### Bit Bags 54 | 55 | Bit Bags are a set of sequential bits, padded to a byte, that are used to map static objects that are 56 | smaller than a byte. For example, consider the following code: 57 | 58 | ```sn 59 | value class Status(lastUpdate: date, value: 'OPEN' | 'CLOSED', public: boolean) 60 | ``` 61 | 62 | Values `value` and `public` can be mapped as two bits, as they are both boolean values internally. 63 | 64 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 65 | |-------|--------|-----|-----|-----|-----|-----|-----| 66 | | value | public | --- | --- | --- | --- | --- | --- | 67 | 68 | By using a padding, we can fit two boolean values in a byte and align it to cacheable memory. However, consider 69 | this following code now: 70 | 71 | ```sn 72 | value class Category(lastUpdate: date, value: 'UNHEALTHY' | 'HEALTHY') 73 | entity class Account(id: Id, category: Category, active: boolean, public: boolean) 74 | ``` 75 | 76 | Now we have some values inside a value object Category, and some values in the root of the entity. However, because 77 | we have **consistent** and **semantic** mutation boundaries at the entity level, we can still remodel and optimise the memory layout and put 78 | all fitting values in a single bit bag. 79 | 80 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 81 | |----------------|--------|--------|-----|-----|-----|-----|-----| 82 | | category.value | active | public | --- | --- | --- | --- | --- | 83 | 84 | In case of an *overspill* the bit bag can grow to the next byte. 85 | 86 | Enums can be mapped using a section of the bit bag bigger than a single bit, for example: 87 | 88 | ```sn 89 | value class Semaphore(state: 'GREEN' | 'YELLOW' | 'RED') 90 | ``` 91 | 92 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 93 | |-----------------------|---------|-----|-----|-----|-----|-----|-----| 94 | | 0 = green, 1 = yellow | 1 = red | --- | --- | --- | --- | --- | --- | 95 | 96 | ### Direct Mapping 97 | 98 | Direct mapping act as an embedded value. Static objects can always be embedded as they 99 | have a fixed size. For example: 100 | 101 | ```sn 102 | value class Amount(value: double, currency: 'USD' | 'EUR') 103 | ``` 104 | 105 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-16 | 106 | |----------|-----|-----|-----|-----|-----|-----|-----|-------| 107 | | currency | --- | --- | --- | --- | --- | --- | --- | value | 108 | 109 | Note the padding, to ensure that all direct mappings are padded at the byte level. If we have more elements 110 | that can fit the bit bag, they will be optimised into the bit bag even if there are direct mappings. 111 | 112 | ```sn 113 | value class Amount(value: double, currency: 'USD' | 'EUR', debt: boolean) 114 | ``` 115 | 116 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-16 | 117 | |----------|-------|-----|-----|-----|-----|-----|-----|-------| 118 | | currency | debt | --- | --- | --- | --- | --- | --- | value | 119 | 120 | ### References 121 | 122 | References are fixed size, but they point to a region in memory. They are used to store overspilled flexible objects. 123 | 124 | ### Paddings 125 | 126 | Bits without specific meaning that are used to pad elements in the memory model to the byte level. 127 | 128 | ## Flexible Packing 129 | 130 | Flexible objects, like strings, can grow to any size in memory. An example are strings that act as labels that can be just 131 | a single word, and texts with descriptions of objects. 132 | 133 | However, for some cases, strings and other objects can be packed into a fixed size. 134 | Packed strings are fit into a direct mapping of fixed size that store the string metadata, allow the co-location 135 | of the string information with the owner entity. 136 | 137 | Consider the following case: 138 | 139 | ```sn 140 | entity class Account(id: int64, active: boolean, public: boolean, owner: string) 141 | ``` 142 | 143 | Assuming a maximum size of 64 bytes per object, we have the following schema: 144 | 145 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16-80 | 80-512 | 146 | |--------|--------|-----|-----------------------|------------|------------|------------|------------|-------|--------| 147 | | active | public | --- | owner.overspilled = 0 | owner.size | owner.size | owner.size | owner.size | id | owner | 148 | 149 | By using this layout, we can co-locate up to 55 bytes (55 ascii characters). But, what happens when we need bigger strings? Here, overspilling happens, and we can do the following: 150 | 151 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16-80 | 80-512 | 152 | |--------|--------|-----|-----------------------|------------|------------|------------|------------|-------|------------| 153 | | active | public | --- | owner.overspilled = 1 | owner.size | owner.size | owner.size | owner.size | id | owner.data | 154 | 155 | Data can be overspilled and packed at the same time for optimization of specific use cases. For example, a queue could pack the latest two values in memory and reference the rest of the values. -------------------------------------------------------------------------------- /lexer/src/lexer.test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lexer.h" 6 | 7 | #define LEXER_PROCESS(var, code) \ 8 | std::stringstream test(code); \ 9 | auto lexer = scc::lexer::lexer(); \ 10 | auto var = lexer.process(test, testing::UnitTest::GetInstance()->current_test_info()->name()) 11 | 12 | #define CONSUME_TOKEN(var, result) \ 13 | auto var = result.front(); \ 14 | result.pop_front() 15 | 16 | #define ASSERT_NEXT_TOKEN(_type) do { \ 17 | auto assertx = result.front(); \ 18 | result.pop_front(); \ 19 | ASSERT_EQ(assertx->type, _type); \ 20 | } while (0) 21 | 22 | TEST(lexer, reads_a_comment_from_a_stream) { 23 | LEXER_PROCESS(result, "; this is a comment\n"); 24 | 25 | CONSUME_TOKEN(comment, result); 26 | ASSERT_EQ(comment->type, scc::lexer::token_type::COMMENT); 27 | ASSERT_EQ(std::get(comment->metadata).content, "; this is a comment"); 28 | 29 | CONSUME_TOKEN(new_line, result); 30 | ASSERT_EQ(new_line->type, scc::lexer::token_type::NEW_LINE); 31 | } 32 | 33 | TEST(lexer, reads_whitespaces) { 34 | LEXER_PROCESS(result, " \t"); 35 | 36 | CONSUME_TOKEN(whitespace, result); 37 | ASSERT_EQ(whitespace->type, scc::lexer::token_type::WHITESPACE); 38 | 39 | CONSUME_TOKEN(tab, result); 40 | ASSERT_EQ(tab->type, scc::lexer::token_type::WHITESPACE); 41 | } 42 | 43 | TEST(lexer, reads_identifiers) { 44 | LEXER_PROCESS(result, "identifier _identifier identifier1337 "); 45 | 46 | CONSUME_TOKEN(first, result); 47 | ASSERT_EQ(first->type, scc::lexer::token_type::IDENTIFIER); 48 | ASSERT_EQ(std::get(first->metadata).content, "identifier"); 49 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 50 | 51 | CONSUME_TOKEN(second, result); 52 | ASSERT_EQ(second->type, scc::lexer::token_type::IDENTIFIER); 53 | ASSERT_EQ(std::get(second->metadata).content, "_identifier"); 54 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 55 | 56 | CONSUME_TOKEN(third, result); 57 | ASSERT_EQ(third->type, scc::lexer::token_type::IDENTIFIER); 58 | ASSERT_EQ(std::get(third->metadata).content, "identifier1337"); 59 | } 60 | 61 | TEST(lexer, reads_integers) { 62 | LEXER_PROCESS(result, "50 0x50 0o50 0b10 "); 63 | 64 | CONSUME_TOKEN(fifty10, result); 65 | ASSERT_EQ(fifty10->type, scc::lexer::token_type::INTEGER); 66 | ASSERT_EQ(std::get(fifty10->metadata).representation, "50"); 67 | ASSERT_EQ(std::get(fifty10->metadata).base, 68 | scc::lexer::numeric_base::DECIMAL); 69 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 70 | 71 | CONSUME_TOKEN(fifty16, result); 72 | ASSERT_EQ(fifty16->type, scc::lexer::token_type::INTEGER); 73 | ASSERT_EQ(std::get(fifty16->metadata).representation, "0x50"); 74 | ASSERT_EQ(std::get(fifty16->metadata).base, 75 | scc::lexer::numeric_base::HEXADECIMAL); 76 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 77 | 78 | CONSUME_TOKEN(fifty8, result); 79 | ASSERT_EQ(fifty8->type, scc::lexer::token_type::INTEGER); 80 | ASSERT_EQ(std::get(fifty8->metadata).representation, "0o50"); 81 | ASSERT_EQ(std::get(fifty8->metadata).base, 82 | scc::lexer::numeric_base::OCTAL); 83 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 84 | 85 | CONSUME_TOKEN(ten2, result); 86 | ASSERT_EQ(ten2->type, scc::lexer::token_type::INTEGER); 87 | ASSERT_EQ(std::get(ten2->metadata).representation, "0b10"); 88 | ASSERT_EQ(std::get(ten2->metadata).base, 89 | scc::lexer::numeric_base::BINARY); 90 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 91 | } 92 | 93 | TEST(lexer, reads_floating_point_decimals) { 94 | LEXER_PROCESS(result, "50.5 "); 95 | 96 | CONSUME_TOKEN(fifty10, result); 97 | ASSERT_EQ(fifty10->type, scc::lexer::token_type::FLOATING); 98 | ASSERT_EQ(std::get(fifty10->metadata).representation, "50.5"); 99 | ASSERT_EQ(std::get(fifty10->metadata).base, 100 | scc::lexer::numeric_base::DECIMAL); 101 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 102 | } 103 | 104 | TEST(lexer, reads_floating_point_decimals_after_a_comma) { 105 | LEXER_PROCESS(result, ", 50.5 "); 106 | 107 | CONSUME_TOKEN(comma, result); 108 | CONSUME_TOKEN(whitespace, result); 109 | 110 | CONSUME_TOKEN(fifty10, result); 111 | ASSERT_EQ(fifty10->type, scc::lexer::token_type::FLOATING); 112 | ASSERT_EQ(std::get(fifty10->metadata).representation, "50.5"); 113 | ASSERT_EQ(std::get(fifty10->metadata).base, 114 | scc::lexer::numeric_base::DECIMAL); 115 | ASSERT_NEXT_TOKEN(scc::lexer::token_type::WHITESPACE); 116 | } 117 | 118 | TEST(lexer, reads_parens_brackets_and_braces) { 119 | LEXER_PROCESS(result, "()[]{} "); 120 | 121 | CONSUME_TOKEN(oparen, result); 122 | ASSERT_EQ(oparen->type, scc::lexer::token_type::OPEN_PAREN); 123 | 124 | CONSUME_TOKEN(cparen, result); 125 | ASSERT_EQ(cparen->type, scc::lexer::token_type::CLOSE_PAREN); 126 | 127 | CONSUME_TOKEN(obracket, result); 128 | ASSERT_EQ(obracket->type, scc::lexer::token_type::OPEN_BRACKET); 129 | 130 | CONSUME_TOKEN(cbracket, result); 131 | ASSERT_EQ(cbracket->type, scc::lexer::token_type::CLOSE_BRACKET); 132 | 133 | CONSUME_TOKEN(obrace, result); 134 | ASSERT_EQ(obrace->type, scc::lexer::token_type::OPEN_BRACE); 135 | 136 | CONSUME_TOKEN(cbrace, result); 137 | ASSERT_EQ(cbrace->type, scc::lexer::token_type::CLOSE_BRACE); 138 | } 139 | 140 | TEST(lexer, reads_separators) { 141 | LEXER_PROCESS(result, ",:. "); 142 | 143 | CONSUME_TOKEN(comma, result); 144 | ASSERT_EQ(comma->type, scc::lexer::token_type::COMMA); 145 | 146 | CONSUME_TOKEN(colon, result); 147 | ASSERT_EQ(colon->type, scc::lexer::token_type::COLON); 148 | 149 | CONSUME_TOKEN(dot, result); 150 | ASSERT_EQ(dot->type, scc::lexer::token_type::DOT); 151 | } 152 | 153 | TEST(lexer, reads_operators) { 154 | LEXER_PROCESS(result, "@#~?!=<>+-*/^%& "); 155 | 156 | CONSUME_TOKEN(at, result); 157 | ASSERT_EQ(at->type, scc::lexer::token_type::AT); 158 | 159 | CONSUME_TOKEN(hash, result); 160 | ASSERT_EQ(hash->type, scc::lexer::token_type::HASH); 161 | 162 | CONSUME_TOKEN(tilde, result); 163 | ASSERT_EQ(tilde->type, scc::lexer::token_type::TILDE); 164 | 165 | CONSUME_TOKEN(qmark, result); 166 | ASSERT_EQ(qmark->type, scc::lexer::token_type::QUESTION_MARK); 167 | 168 | CONSUME_TOKEN(emark, result); 169 | ASSERT_EQ(emark->type, scc::lexer::token_type::EXCLAMATION_MARK); 170 | 171 | CONSUME_TOKEN(equals, result); 172 | ASSERT_EQ(equals->type, scc::lexer::token_type::EQUALS); 173 | 174 | CONSUME_TOKEN(lt, result); 175 | ASSERT_EQ(lt->type, scc::lexer::token_type::LESS_THAN); 176 | 177 | CONSUME_TOKEN(gt, result); 178 | ASSERT_EQ(gt->type, scc::lexer::token_type::GREATER_THAN); 179 | 180 | CONSUME_TOKEN(plus, result); 181 | ASSERT_EQ(plus->type, scc::lexer::token_type::PLUS); 182 | 183 | CONSUME_TOKEN(minus, result); 184 | ASSERT_EQ(minus->type, scc::lexer::token_type::MINUS); 185 | 186 | CONSUME_TOKEN(multiply, result); 187 | ASSERT_EQ(multiply->type, scc::lexer::token_type::MULTIPLY); 188 | 189 | CONSUME_TOKEN(divide, result); 190 | ASSERT_EQ(divide->type, scc::lexer::token_type::DIVIDE); 191 | 192 | CONSUME_TOKEN(caret, result); 193 | ASSERT_EQ(caret->type, scc::lexer::token_type::CARET); 194 | 195 | CONSUME_TOKEN(percent, result); 196 | ASSERT_EQ(percent->type, scc::lexer::token_type::PERCENT); 197 | 198 | CONSUME_TOKEN(ampersand, result); 199 | ASSERT_EQ(ampersand->type, scc::lexer::token_type::AMPERSAND); 200 | } 201 | 202 | TEST(lexer, reads_quotes) { 203 | LEXER_PROCESS(result, "'abc'"); 204 | 205 | CONSUME_TOKEN(str, result); 206 | ASSERT_EQ(str->type, scc::lexer::token_type::STRING); 207 | ASSERT_EQ(std::get(str->metadata).content, "abc"); 208 | } -------------------------------------------------------------------------------- /passes/src/mutations/pass_entity_class_ir_transformer.cpp: -------------------------------------------------------------------------------- 1 | #include "pass_entity_class_ir_transformer.h" 2 | 3 | namespace scc::passes::mutations { 4 | using namespace scc::ast; 5 | using namespace scc::ast::ir; 6 | using namespace scc::type_system::memory; 7 | 8 | static shared_ptr select_field_from_type(std::shared_ptr &type, std::list::iterator begin, std::list::iterator end); 9 | 10 | pass_entity_class_ir_transformer::pass_entity_class_ir_transformer(const std::shared_ptr &types) 11 | : types(types) { 12 | 13 | } 14 | 15 | pass_entity_class_ir_transformer::~pass_entity_class_ir_transformer() = default; 16 | 17 | void pass_entity_class_ir_transformer::execute(ast::ast_root &root) const { 18 | list new_children; 19 | 20 | for (auto &child : root->children) { 21 | if (std::dynamic_pointer_cast(child)) { 22 | auto nklass = std::dynamic_pointer_cast(child); 23 | if (nklass->type != ast::nclass_type::ENTITY) { 24 | continue; 25 | } 26 | 27 | auto ntype = types->resolve(nklass->name); 28 | 29 | // ----- create struct ----- 30 | auto nstr = std::make_shared(); 31 | nstr->name = nklass->name; 32 | 33 | // define struct fields 34 | for (auto &s : ntype->layout.storages) { 35 | if (std::holds_alternative(s)) { 36 | auto bb = std::get(s); 37 | nstr->fields.emplace_back(nstruct_field { .field_type = ast::ir::BIT_BAG, .name = "bit_bag", .size = bb.size }); 38 | } 39 | } 40 | 41 | new_children.emplace_back(nstr); 42 | // ----- spawn function ----- 43 | auto spawn_fn = std::make_shared(); 44 | spawn_fn->struct_name = nklass->name; 45 | spawn_fn->name = nklass->name + "_spawn"; 46 | spawn_fn->retval = OBJECT; 47 | spawn_fn->body = std::make_shared(); 48 | 49 | auto malloc_fn_call = std::make_shared(); 50 | malloc_fn_call->type = nklass->name; 51 | spawn_fn->body->children.emplace_back(malloc_fn_call); 52 | 53 | new_children.emplace_back(spawn_fn); 54 | // ----- free function ----- 55 | auto free_fn = std::make_shared(); 56 | free_fn->struct_name = nklass->name; 57 | free_fn->name = nklass->name + "_free"; 58 | free_fn->retval = VOID; 59 | free_fn->body = std::make_shared(); 60 | 61 | auto free_fn_call = std::make_shared(); 62 | free_fn->body->children.emplace_back(free_fn_call); 63 | 64 | new_children.emplace_back(free_fn); 65 | // ----- transform methods ----- 66 | if (nklass->body.has_value()) { 67 | for (auto &m : nklass->body.value()->children) { 68 | if (std::dynamic_pointer_cast(m)) { 69 | auto nfn = std::dynamic_pointer_cast(m); 70 | auto nstrf = std::make_shared(); 71 | 72 | nstrf->struct_name = nklass->name; 73 | nstrf->name = nklass->name + "_" + nfn->name; 74 | nstrf->body = std::make_shared(); 75 | 76 | parse_self_refs(ntype, nfn->body.value(), nstrf->body); 77 | new_children.emplace_back(nstrf); 78 | } else { 79 | new_children.emplace_back(m); 80 | } 81 | } 82 | } 83 | } else { 84 | new_children.emplace_back(child); 85 | } 86 | } 87 | 88 | root->children = new_children; 89 | } 90 | 91 | diagnostic::diagnostic_phase_id pass_entity_class_ir_transformer::pass_phase() const { 92 | return diagnostic::diagnostic_phase_id::PASS_ENTITY_CLASS_IR_TRANSFORMER; 93 | } 94 | 95 | expression_ref pass_entity_class_ir_transformer::parse_self_refs(std::shared_ptr &type, expression_ref &expr, ast_block &block) const { 96 | if (std::dynamic_pointer_cast(expr)) { 97 | auto selfset = std::dynamic_pointer_cast(expr); 98 | shared_ptr fieldef = select_field_from_type(type, selfset->selector.begin(), selfset->selector.end()); 99 | 100 | switch (fieldef->selector.type) { 101 | case type_system::memory::selector_type::BIT_BAG: { 102 | auto bbset = std::make_shared(); 103 | bbset->value = selfset->value; 104 | bbset->bit = fieldef->selector.offset; 105 | 106 | block->children.emplace_back(bbset); 107 | return bbset; 108 | } break; 109 | case type_system::memory::selector_type::DIRECT: { 110 | auto ddset = std::make_shared(); 111 | ddset->value = selfset->value; 112 | ddset->index = fieldef->selector.offset; 113 | 114 | block->children.emplace_back(ddset); 115 | return ddset; 116 | } break; 117 | } 118 | } else if (std::dynamic_pointer_cast(expr)) { 119 | auto selfget = std::dynamic_pointer_cast(expr); 120 | shared_ptr fieldef = select_field_from_type(type, selfget->selector.begin(), selfget->selector.end()); 121 | 122 | switch (fieldef->selector.type) { 123 | case type_system::memory::selector_type::BIT_BAG: { 124 | auto bbget = std::make_shared(); 125 | bbget->bit = fieldef->selector.offset; 126 | 127 | block->children.emplace_back(bbget); 128 | return bbget; 129 | } break; 130 | case type_system::memory::selector_type::DIRECT: { 131 | auto ddget = std::make_shared(); 132 | ddget->index = fieldef->selector.offset; 133 | 134 | block->children.emplace_back(ddget); 135 | return ddget; 136 | } break; 137 | } 138 | } else if (std::dynamic_pointer_cast(expr)) { 139 | auto fncall = std::dynamic_pointer_cast(expr); 140 | list> mapped_args; 141 | for (auto arg : fncall->arguments) { 142 | if (std::holds_alternative(arg)) { 143 | auto narg = std::get(arg); 144 | narg->expression = parse_self_refs(type, narg->expression, block); 145 | mapped_args.emplace_back(narg); 146 | } else { 147 | auto earg = std::get(arg); 148 | mapped_args.emplace_back(parse_self_refs(type, earg, block)); 149 | } 150 | } 151 | fncall->arguments = mapped_args; 152 | block->children.emplace_back(fncall); 153 | return fncall; 154 | } 155 | 156 | block->children.emplace_back(expr); 157 | return expr; 158 | } 159 | 160 | static shared_ptr select_field_from_type(std::shared_ptr &type, std::list::iterator begin, std::list::iterator end) { 161 | auto cur = *begin; 162 | 163 | for (auto &f : type->fields) { 164 | if (f->name == cur) { 165 | if (++begin == end) { 166 | return f; 167 | } 168 | 169 | return select_field_from_type(f->base_type, begin, end); 170 | } 171 | } 172 | 173 | return nullptr; 174 | } 175 | } 176 | 177 | -------------------------------------------------------------------------------- /parser/src/test-includes/includes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../parser.h" 7 | #include "lexer.h" 8 | #include "ast.h" 9 | 10 | namespace scc::parser::test { 11 | using std::shared_ptr; 12 | 13 | template 14 | inline shared_ptr child_nth(const ast::ast_root &root) { 15 | int idx = 0; 16 | for (auto &x : root->children) { 17 | if (idx == ChildIndex) { 18 | return std::dynamic_pointer_cast(x); 19 | } 20 | 21 | idx++; 22 | } 23 | 24 | return nullptr; 25 | } 26 | 27 | template 28 | inline shared_ptr field_nth(const shared_ptr &root) { 29 | int idx = 0; 30 | for (auto &x : root->fields) { 31 | if (idx == ChildIndex) { 32 | return x; 33 | } 34 | 35 | idx++; 36 | } 37 | 38 | return nullptr; 39 | } 40 | 41 | template 42 | inline shared_ptr child_nth(const shared_ptr &root) { 43 | int idx = 0; 44 | if (!root->body.has_value()) { 45 | return nullptr; 46 | } 47 | 48 | for (auto &x : root->body.value()->children) { 49 | if (idx == ChildIndex) { 50 | return std::dynamic_pointer_cast(x); 51 | } 52 | 53 | idx++; 54 | } 55 | 56 | return nullptr; 57 | } 58 | 59 | template 60 | inline shared_ptr body_of(const shared_ptr &root) { 61 | return std::dynamic_pointer_cast(root->expression.value()); 62 | } 63 | 64 | template 65 | inline shared_ptr left_op(const shared_ptr &root) { 66 | return std::dynamic_pointer_cast(root->left); 67 | } 68 | 69 | template 70 | inline shared_ptr left_op(const shared_ptr &root) { 71 | return std::dynamic_pointer_cast(root->left); 72 | } 73 | 74 | template 75 | inline std::shared_ptr::value, ArgType>::type> 76 | argument_nth(const shared_ptr &root) { 77 | int idx = 0; 78 | for (auto &x : root->arguments) { 79 | if (idx == ChildIndex) { 80 | if (std::holds_alternative(x)) { 81 | return std::dynamic_pointer_cast(std::get(x)); 82 | } 83 | break; 84 | } 85 | 86 | idx++; 87 | } 88 | 89 | return nullptr; 90 | } 91 | 92 | template 93 | inline std::shared_ptr::value, ArgType>::type> 94 | argument_nth(const shared_ptr &root) { 95 | int idx = 0; 96 | for (auto &x : root->arguments) { 97 | if (idx == ChildIndex) { 98 | if (std::holds_alternative(x)) { 99 | return std::dynamic_pointer_cast(std::get(x)); 100 | } 101 | break; 102 | } 103 | 104 | idx++; 105 | } 106 | 107 | return nullptr; 108 | } 109 | 110 | template 111 | inline std::shared_ptr::value, ArgType>::type> 112 | argument_nth(const shared_ptr &root) { 113 | int idx = 0; 114 | for (auto &x : root->arguments) { 115 | if (idx == ChildIndex) { 116 | if (std::holds_alternative(x)) { 117 | return std::dynamic_pointer_cast(std::get(x)); 118 | } 119 | break; 120 | } 121 | 122 | idx++; 123 | } 124 | 125 | return nullptr; 126 | } 127 | 128 | template 129 | shared_ptr::value, ArgType>::type> 130 | inline argument_nth(const shared_ptr &root) { 131 | int idx = 0; 132 | for (auto &x : root->arguments) { 133 | if (idx == ChildIndex) { 134 | if (std::holds_alternative(x)) { 135 | return std::get(x); 136 | } 137 | 138 | break; 139 | } 140 | 141 | idx++; 142 | } 143 | 144 | return nullptr; 145 | } 146 | 147 | template 148 | shared_ptr::value, ArgType>::type> 149 | inline argument_nth(const shared_ptr &root) { 150 | int idx = 0; 151 | for (auto &x : root->arguments) { 152 | if (idx == ChildIndex) { 153 | if (std::holds_alternative(x)) { 154 | return std::get(x); 155 | } 156 | 157 | break; 158 | } 159 | 160 | idx++; 161 | } 162 | 163 | return nullptr; 164 | } 165 | 166 | template 167 | shared_ptr::value, ArgType>::type> 168 | inline argument_nth(const shared_ptr &root) { 169 | int idx = 0; 170 | for (auto &x : root->arguments) { 171 | if (idx == ChildIndex) { 172 | if (std::holds_alternative(x)) { 173 | return std::get(x); 174 | } 175 | 176 | break; 177 | } 178 | 179 | idx++; 180 | } 181 | 182 | return nullptr; 183 | } 184 | 185 | template 186 | inline std::shared_ptr argument_value(const nfunction_call_named_argument_ref &root) { 187 | return std::dynamic_pointer_cast(root->expression); 188 | } 189 | 190 | inline bool has_body(const shared_ptr &root) { 191 | return root->expression.has_value(); 192 | } 193 | 194 | inline bool has_body(const shared_ptr &root) { 195 | return root->body.has_value(); 196 | } 197 | 198 | inline bool has_parameters(const shared_ptr &root) { 199 | return !root->parameters.empty(); 200 | } 201 | 202 | template 203 | inline std::shared_ptr::value, ArgType>::type> 204 | parameter_nth(const shared_ptr &root) { 205 | int idx = 0; 206 | for (auto &x : root->parameters) { 207 | if (idx == ChildIndex) { 208 | if (std::holds_alternative(x)) { 209 | return std::dynamic_pointer_cast(std::get(x)); 210 | } 211 | break; 212 | } 213 | 214 | idx++; 215 | } 216 | 217 | return nullptr; 218 | } 219 | 220 | template 221 | shared_ptr::value, ArgType>::type> 222 | inline parameter_nth(const shared_ptr &root) { 223 | int idx = 0; 224 | for (auto &x : root->parameters) { 225 | if (idx == ChildIndex) { 226 | if (std::holds_alternative(x)) { 227 | return std::get(x); 228 | } 229 | 230 | break; 231 | } 232 | 233 | idx++; 234 | } 235 | 236 | return nullptr; 237 | } 238 | 239 | template 240 | inline ChildType generic_parameter_nth(const type_constraints &root) { 241 | int idx = 0; 242 | 243 | for (auto &x : get(root).parameters) { 244 | if (idx == ChildIndex) { 245 | return get(x); 246 | } 247 | 248 | idx++; 249 | } 250 | 251 | throw std::runtime_error(std::string("Could not find a generic parameter with type ") + typeid(ChildType).name()); 252 | } 253 | 254 | inline ast::ast_root parse(const string &code) { 255 | scc::lexer::lexer _lexer; 256 | scc::parser::parser _parser; 257 | std::stringstream ss(code); 258 | 259 | return _parser.parse(_lexer.process(ss, code)); 260 | } 261 | } -------------------------------------------------------------------------------- /diagnostic/src/diagnostic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "diagnostic.h" 8 | 9 | using json = nlohmann::json; 10 | 11 | static std::unique_ptr S_diagnostic; 12 | static std::shared_ptr S_current_phase; 13 | static bool had_error = false; 14 | 15 | using std::cout; 16 | using std::endl; 17 | using std::put_time; 18 | 19 | using namespace std::chrono; 20 | 21 | static unsigned long now() { 22 | auto now = std::chrono::high_resolution_clock::now(); 23 | return now.time_since_epoch().count(); 24 | } 25 | 26 | static const char *phase_id_to_string(scc::diagnostic::diagnostic_phase_id id) { 27 | switch (id) { 28 | case scc::diagnostic::diagnostic_phase_id::DISCOVERY: 29 | return "DISCOVERY"; 30 | case scc::diagnostic::diagnostic_phase_id::LEXER: 31 | return "LEXER"; 32 | case scc::diagnostic::diagnostic_phase_id::PARSER: 33 | return "PARSER"; 34 | case scc::diagnostic::diagnostic_phase_id::GENERATE_LLVM_IR: 35 | return "GENERATE_LLVM_IR"; 36 | case scc::diagnostic::diagnostic_phase_id::OPTIMIZE_LLVM_IR: 37 | return "OPTIMIZE_LLVM_IR"; 38 | case scc::diagnostic::diagnostic_phase_id::PASS_DETECT_CLASSES: 39 | return "PASS_DETECT_CLASSES"; 40 | case scc::diagnostic::diagnostic_phase_id::PASS_INTERNAL_MODELER: 41 | return "PASS_INTERNAL_MODELER"; 42 | case scc::diagnostic::diagnostic_phase_id::PASS_VALUE_CLASS_IR_TRANSFORMER: 43 | return "PASS_VALUE_CLASS_IR_TRANSFORMER"; 44 | case scc::diagnostic::diagnostic_phase_id::PASS_ENTITY_CLASS_IR_TRANSFORMER: 45 | return "PASS_ENTITY_CLASS_IR_TRANSFORMER"; 46 | case scc::diagnostic::diagnostic_phase_id::PASS_ENTITY_METHOD_RESOLUTION: 47 | return "PASS_ENTITY_METHOD_RESOLUTION"; 48 | case scc::diagnostic::diagnostic_phase_id::EMIT_LLVM: 49 | return "EMIT_LLVM"; 50 | } 51 | 52 | return "UNKNOWN?"; 53 | } 54 | 55 | static const char *log_level_to_string(scc::diagnostic::diagnostic_log_level level) { 56 | switch (level) { 57 | case scc::diagnostic::diagnostic_log_level::DEBUG: 58 | return "DEBUG"; 59 | case scc::diagnostic::diagnostic_log_level::INFO: 60 | return "INFO "; 61 | case scc::diagnostic::diagnostic_log_level::WARN: 62 | return "WARN "; 63 | case scc::diagnostic::diagnostic_log_level::ERROR: 64 | return "ERROR"; 65 | case scc::diagnostic::diagnostic_log_level::FATAL: 66 | return "FATAL"; 67 | } 68 | 69 | return "DEBUG"; 70 | } 71 | 72 | static const char *log_level_to_string_unpad(scc::diagnostic::diagnostic_log_level level) { 73 | switch (level) { 74 | case scc::diagnostic::diagnostic_log_level::DEBUG: 75 | return "DEBUG"; 76 | case scc::diagnostic::diagnostic_log_level::INFO: 77 | return "INFO"; 78 | case scc::diagnostic::diagnostic_log_level::WARN: 79 | return "WARN"; 80 | case scc::diagnostic::diagnostic_log_level::ERROR: 81 | return "ERROR"; 82 | case scc::diagnostic::diagnostic_log_level::FATAL: 83 | return "FATAL"; 84 | } 85 | 86 | return "DEBUG"; 87 | } 88 | 89 | 90 | namespace scc::diagnostic { 91 | void initialize(const diagnostic_log_level allowed_level) { 92 | S_diagnostic = std::make_unique(); 93 | 94 | S_diagnostic->allowed_level = allowed_level; 95 | S_diagnostic->scc_version = "0.0.1"; 96 | S_diagnostic->os_version = "Linux"; 97 | S_diagnostic->start = now(); 98 | S_diagnostic->total_errors = 0; 99 | S_diagnostic->total_warnings = 0; 100 | } 101 | 102 | void start_phase(const diagnostic_phase_id phase) { 103 | if (S_diagnostic == nullptr) { // this happens on tests, so run on verbose mode 104 | initialize(diagnostic_log_level::DEBUG); 105 | } 106 | 107 | S_current_phase = std::make_shared(); 108 | S_current_phase->id = phase; 109 | S_current_phase->start = now(); 110 | S_current_phase->errors = 0; 111 | S_current_phase->warnings = 0; 112 | } 113 | 114 | void end_phase() { 115 | S_current_phase->end = now(); 116 | S_diagnostic->total_errors += S_current_phase->errors; 117 | S_diagnostic->total_warnings += S_current_phase->warnings; 118 | S_diagnostic->phases.push_back(S_current_phase); 119 | S_current_phase = nullptr; 120 | } 121 | 122 | void log(const diagnostic_log_level level, const string &format, const initializer_list markers) { 123 | if (level >= diagnostic_log_level::ERROR) { 124 | had_error = true; 125 | } 126 | 127 | auto log_msg = std::make_shared(); 128 | if (level == diagnostic_log_level::WARN) { 129 | S_current_phase->warnings++; 130 | } else if (level == diagnostic_log_level::ERROR || level == diagnostic_log_level::FATAL) { 131 | S_current_phase->errors++; 132 | } 133 | 134 | log_msg->when = now(); 135 | log_msg->level = level; 136 | log_msg->message = format; 137 | log_msg->markers.assign(markers); 138 | S_current_phase->logs.push_back(log_msg); 139 | } 140 | 141 | void finish() { 142 | S_diagnostic->end = now(); 143 | } 144 | 145 | void dump_diagnostic(const string &where) { 146 | std::ofstream out(where); 147 | 148 | json dump = json(); 149 | dump["allowed_level"] = log_level_to_string_unpad(S_diagnostic->allowed_level); 150 | dump["scc_version"] = S_diagnostic->scc_version; 151 | dump["os_version"] = S_diagnostic->os_version; 152 | dump["start"] = S_diagnostic->start; 153 | dump["end"] = S_diagnostic->start; 154 | dump["total_errors"] = S_diagnostic->total_errors; 155 | dump["total_warnings"] = S_diagnostic->total_warnings; 156 | std::vector phases; 157 | for (const auto &phase : S_diagnostic->phases) { 158 | auto pj = json(); 159 | pj["phase"] = phase_id_to_string(phase->id); 160 | pj["start"] = S_diagnostic->start; 161 | pj["end"] = S_diagnostic->start; 162 | pj["errors"] = phase->errors; 163 | pj["warnings"] = phase->warnings; 164 | 165 | std::vector logs; 166 | for (const auto &log : phase->logs) { 167 | json lj = json(); 168 | lj["level"] = log_level_to_string_unpad(log->level); 169 | lj["when"] = log->when; 170 | lj["message"] = log->message; 171 | 172 | std::vector markers; 173 | for (const auto &marker : log->markers) { 174 | json mj = json(); 175 | mj["key"] = marker.key; 176 | mj["value"] = marker.value; 177 | 178 | markers.push_back(mj); 179 | } 180 | 181 | lj["markers"] = markers; 182 | logs.push_back(lj); 183 | } 184 | 185 | pj["logs"] = logs; 186 | phases.push_back(pj); 187 | } 188 | 189 | dump["phases"] = phases; 190 | out << dump.dump(2) << std::endl; 191 | out.close(); 192 | } 193 | 194 | void print_user_diagnostic() { 195 | cout << "[" << S_diagnostic->start << "] GLOBAL : scc version " << S_diagnostic->scc_version << " and os version " << S_diagnostic->os_version << endl; 196 | for (const auto &phase : S_diagnostic->phases) { 197 | auto phase_name = phase_id_to_string(phase->id); 198 | 199 | for (const auto &log : phase->logs) { 200 | if (log->level >= S_diagnostic->allowed_level) { 201 | cout << "[" << log->when << "] " << log_level_to_string(log->level) << " " << phase_name << ": " << log->message << " | "; 202 | for (const auto &marker : log->markers) { 203 | cout << marker.key << "=" << marker.value << " | "; 204 | } 205 | cout << endl; 206 | } 207 | } 208 | } 209 | 210 | auto end = S_diagnostic->end; 211 | auto start = S_diagnostic->start; 212 | 213 | double seconds = ((double) end - (double) start) / 1000.0 / 1000.0 / 1000.0; 214 | seconds = ceil(seconds * 100.0) / 100.0; 215 | 216 | cout << "[" << S_diagnostic->start << "] GLOBAL : Compilation finished "; 217 | 218 | if (S_diagnostic->total_errors > 0) { 219 | cout << "with " << S_diagnostic->total_errors << " errors "; 220 | 221 | if (S_diagnostic->total_warnings > 0) { 222 | cout << "and "; 223 | } 224 | } 225 | 226 | if (S_diagnostic->total_warnings > 0) { 227 | cout << S_diagnostic->total_warnings << " warnings "; 228 | } 229 | 230 | cout << "in " << seconds << " seconds." << endl; 231 | } 232 | 233 | int return_code() { 234 | return had_error ? 1 : 0; 235 | } 236 | 237 | bool debug() { 238 | return S_diagnostic->allowed_level == diagnostic_log_level::DEBUG; 239 | } 240 | } --------------------------------------------------------------------------------