From 0eeea712d1085e0e7b19ff8b8a0ee00a28446b65 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 12 Oct 2019 23:31:43 +0800 Subject: [PATCH 001/129] get rid of c-style casts...? --- .travis.yml | 2 +- appveyor.yml | 2 +- external/mpreal/mpreal.h | 2814 ++++++++++----------- makefile | 4 +- source/backend/interp/driver.cpp | 2 +- source/backend/llvm/linker.cpp | 12 +- source/backend/llvm/translator.cpp | 33 +- source/codegen/alloc.cpp | 3 +- source/codegen/arithmetic.cpp | 2 +- source/codegen/constructor.cpp | 2 +- source/codegen/function.cpp | 2 - source/codegen/literals.cpp | 4 +- source/fir/ConstantValue.cpp | 4 +- source/fir/IRBuilder.cpp | 26 +- source/fir/Types/ClassType.cpp | 2 +- source/fir/Types/SingleTypes.cpp | 8 +- source/fir/Types/Type.cpp | 4 +- source/fir/interp/compiler.cpp | 2 +- source/fir/interp/interpreter.cpp | 129 +- source/fir/interp/wrappers.cpp | 2 +- source/frontend/errors.cpp | 4 +- source/frontend/lexer.cpp | 2 +- source/frontend/parser/expr.cpp | 2 +- source/frontend/parser/literal.cpp | 2 +- source/frontend/parser/toplevel.cpp | 10 +- source/include/backend.h | 2 +- source/include/backends/llvm.h | 6 + source/include/container.h | 6 +- source/include/defs.h | 4 +- source/include/ir/constant.h | 17 +- source/include/lexer.h | 2 +- source/include/sst.h | 1 - source/include/sst_expr.h | 1 + source/include/utils.h | 11 + source/main.cpp | 15 +- source/misc/allocator.cpp | 2 +- source/misc/identifier.cpp | 2 +- source/platform/platform.cpp | 24 +- source/typecheck/classes.cpp | 13 - source/typecheck/dotop.cpp | 6 +- source/typecheck/function.cpp | 2 +- source/typecheck/polymorph/solver.cpp | 2 +- source/typecheck/polymorph/transforms.cpp | 6 +- source/typecheck/resolver/driver.cpp | 2 +- source/typecheck/traits.cpp | 2 +- 45 files changed, 1620 insertions(+), 1585 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3cb7bcc1..8c56fc98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ script: CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION LLVM_CONFIG=llvm-config-7 make -j2 build; fi - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend interp build/tester.flx + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile build/tester.flx && ./tester notifications: diff --git a/appveyor.yml b/appveyor.yml index a3278377..3beda0d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,7 +40,7 @@ test_script: - ps: New-Item -Force -Path build\sysroot\usr\local\lib\flaxlibs -ItemType Directory - ps: Copy-Item -Recurse -Force libs\* build\sysroot\usr\local\lib\flaxlibs\ - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend llvm build\tester.flx - - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend interp build\tester.flx + - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot --ffi-escape -run -backend interp build\tester.flx - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot build\tester.flx - ps: .\tester.exe diff --git a/external/mpreal/mpreal.h b/external/mpreal/mpreal.h index 1b990ac4..b3d9b943 100644 --- a/external/mpreal/mpreal.h +++ b/external/mpreal/mpreal.h @@ -1,48 +1,48 @@ /* - MPFR C++: Multi-precision floating point number class for C++. - Based on MPFR library: http://mpfr.org - - Project homepage: http://www.holoborodko.com/pavel/mpfr - Contact e-mail: pavel@holoborodko.com - - Copyright (c) 2008-2015 Pavel Holoborodko - - Contributors: - Dmitriy Gubanov, Konstantin Holoborodko, Brian Gladman, - Helmut Jarausch, Fokko Beekhof, Ulrich Mutze, Heinz van Saanen, - Pere Constans, Peter van Hoof, Gael Guennebaud, Tsai Chia Cheng, - Alexei Zubanov, Jauhien Piatlicki, Victor Berger, John Westwood, - Petr Aleksandrov, Orion Poplawski, Charles Karney, Arash Partow, - Rodney James, Jorge Leitao. - - Licensing: - (A) MPFR C++ is under GNU General Public License ("GPL"). - - (B) Non-free licenses may also be purchased from the author, for users who - do not want their programs protected by the GPL. - - The non-free licenses are for users that wish to use MPFR C++ in - their products but are unwilling to release their software - under the GPL (which would require them to release source code - and allow free redistribution). - - Such users can purchase an unlimited-use license from the author. - Contact us for more details. - - GNU General Public License ("GPL") copyright permissions statement: - ************************************************************************** - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + MPFR C++: Multi-precision floating point number class for C++. + Based on MPFR library: http://mpfr.org + + Project homepage: http://www.holoborodko.com/pavel/mpfr + Contact e-mail: pavel@holoborodko.com + + Copyright (c) 2008-2015 Pavel Holoborodko + + Contributors: + Dmitriy Gubanov, Konstantin Holoborodko, Brian Gladman, + Helmut Jarausch, Fokko Beekhof, Ulrich Mutze, Heinz van Saanen, + Pere Constans, Peter van Hoof, Gael Guennebaud, Tsai Chia Cheng, + Alexei Zubanov, Jauhien Piatlicki, Victor Berger, John Westwood, + Petr Aleksandrov, Orion Poplawski, Charles Karney, Arash Partow, + Rodney James, Jorge Leitao. + + Licensing: + (A) MPFR C++ is under GNU General Public License ("GPL"). + + (B) Non-free licenses may also be purchased from the author, for users who + do not want their programs protected by the GPL. + + The non-free licenses are for users that wish to use MPFR C++ in + their products but are unwilling to release their software + under the GPL (which would require them to release source code + and allow free redistribution). + + Such users can purchase an unlimited-use license from the author. + Contact us for more details. + + GNU General Public License ("GPL") copyright permissions statement: + ************************************************************************** + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifndef __MPREAL_H__ @@ -63,8 +63,8 @@ // Options #define MPREAL_HAVE_MSVC_DEBUGVIEW // Enable Debugger Visualizer for "Debug" builds in MSVC. #define MPREAL_HAVE_DYNAMIC_STD_NUMERIC_LIMITS // Enable extended std::numeric_limits specialization. - // Meaning that "digits", "round_style" and similar members are defined as functions, not constants. - // See std::numeric_limits at the end of the file for more information. + // Meaning that "digits", "round_style" and similar members are defined as functions, not constants. + // See std::numeric_limits at the end of the file for more information. // Library version #define MPREAL_VERSION_MAJOR 3 @@ -80,57 +80,55 @@ #else #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #pragma GCC diagnostic ignored "-Wold-style-cast" #endif - - - // Detect compiler using signatures from http://predef.sourceforge.net/ #if defined(__GNUC__) && defined(__INTEL_COMPILER) - #define IsInf(x) isinf(x) // Intel ICC compiler on Linux + #define IsInf(x) isinf(x) // Intel ICC compiler on Linux #elif defined(_MSC_VER) // Microsoft Visual C++ - #define IsInf(x) (!_finite(x)) + #define IsInf(x) (!_finite(x)) #else - #define IsInf(x) std::isinf(x) // GNU C/C++ (and/or other compilers), just hope for C99 conformance + #define IsInf(x) std::isinf(x) // GNU C/C++ (and/or other compilers), just hope for C99 conformance #endif // A Clang feature extension to determine compiler features. #ifndef __has_feature - #define __has_feature(x) 0 + #define __has_feature(x) 0 #endif // Detect support for r-value references (move semantic). Borrowed from Eigen. #if (__has_feature(cxx_rvalue_references) || \ - defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ - (defined(_MSC_VER) && _MSC_VER >= 1600)) + defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1600)) - #define MPREAL_HAVE_MOVE_SUPPORT + #define MPREAL_HAVE_MOVE_SUPPORT - // Use fields in mpfr_t structure to check if it was initialized / set dummy initialization - #define mpfr_is_initialized(x) (0 != (x)->_mpfr_d) - #define mpfr_set_uninitialized(x) ((x)->_mpfr_d = 0 ) + // Use fields in mpfr_t structure to check if it was initialized / set dummy initialization + #define mpfr_is_initialized(x) (0 != (x)->_mpfr_d) + #define mpfr_set_uninitialized(x) ((x)->_mpfr_d = 0 ) #endif // Detect support for explicit converters. #if (__has_feature(cxx_explicit_conversions) || \ - (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC_MINOR >= 5) || __cplusplus >= 201103L || \ - (defined(_MSC_VER) && _MSC_VER >= 1800)) + (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC_MINOR >= 5) || __cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1800)) - #define MPREAL_HAVE_EXPLICIT_CONVERTERS + #define MPREAL_HAVE_EXPLICIT_CONVERTERS #endif #define MPFR_USE_INTMAX_T // Enable 64-bit integer types - should be defined before mpfr.h #if defined(MPREAL_HAVE_MSVC_DEBUGVIEW) && defined(_MSC_VER) && defined(_DEBUG) - #define MPREAL_MSVC_DEBUGVIEW_CODE DebugView = toString(); - #define MPREAL_MSVC_DEBUGVIEW_DATA std::string DebugView; + #define MPREAL_MSVC_DEBUGVIEW_CODE DebugView = toString(); + #define MPREAL_MSVC_DEBUGVIEW_DATA std::string DebugView; #else - #define MPREAL_MSVC_DEBUGVIEW_CODE - #define MPREAL_MSVC_DEBUGVIEW_DATA + #define MPREAL_MSVC_DEBUGVIEW_CODE + #define MPREAL_MSVC_DEBUGVIEW_DATA #endif @@ -140,13 +138,13 @@ #if (MPFR_VERSION < MPFR_VERSION_NUM(3,0,0)) - #include // Needed for random() + #include // Needed for random() #endif // Less important options #define MPREAL_DOUBLE_BITS_OVERFLOW -1 // Triggers overflow exception during conversion to double if mpreal - // cannot fit in MPREAL_DOUBLE_BITS_OVERFLOW bits - // = -1 disables overflow checks (default) + // cannot fit in MPREAL_DOUBLE_BITS_OVERFLOW bits + // = -1 disables overflow checks (default) // Fast replacement for mpfr_set_zero(x, +1): // (a) uses low-level data members, might not be compatible with new versions of MPFR @@ -163,426 +161,426 @@ namespace mpfr { class mpreal { private: - mpfr_t mp; + mpfr_t mp; public: - // Get default rounding mode & precision - inline static mp_rnd_t get_default_rnd() { return (mp_rnd_t)(mpfr_get_default_rounding_mode()); } - inline static mp_prec_t get_default_prec() { return mpfr_get_default_prec(); } - - // Constructors && type conversions - mpreal(); - mpreal(const mpreal& u); - mpreal(const mpf_t u); - mpreal(const mpz_t u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const mpq_t u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const double u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const long double u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const unsigned long long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const long long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const unsigned long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const unsigned int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); - - // Construct mpreal from mpfr_t structure. - // shared = true allows to avoid deep copy, so that mpreal and 'u' share the same data & pointers. - mpreal(const mpfr_t u, bool shared = false); - - mpreal(const char* s, mp_prec_t prec = mpreal::get_default_prec(), int base = 10, mp_rnd_t mode = mpreal::get_default_rnd()); - mpreal(const std::string& s, mp_prec_t prec = mpreal::get_default_prec(), int base = 10, mp_rnd_t mode = mpreal::get_default_rnd()); - - ~mpreal(); + // Get default rounding mode & precision + inline static mp_rnd_t get_default_rnd() { return (mp_rnd_t)(mpfr_get_default_rounding_mode()); } + inline static mp_prec_t get_default_prec() { return mpfr_get_default_prec(); } + + // Constructors && type conversions + mpreal(); + mpreal(const mpreal& u); + mpreal(const mpf_t u); + mpreal(const mpz_t u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const mpq_t u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const double u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const long double u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const unsigned long long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const long long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const unsigned long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const unsigned int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const long int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const int u, mp_prec_t prec = mpreal::get_default_prec(), mp_rnd_t mode = mpreal::get_default_rnd()); + + // Construct mpreal from mpfr_t structure. + // shared = true allows to avoid deep copy, so that mpreal and 'u' share the same data & pointers. + mpreal(const mpfr_t u, bool shared = false); + + mpreal(const char* s, mp_prec_t prec = mpreal::get_default_prec(), int base = 10, mp_rnd_t mode = mpreal::get_default_rnd()); + mpreal(const std::string& s, mp_prec_t prec = mpreal::get_default_prec(), int base = 10, mp_rnd_t mode = mpreal::get_default_rnd()); + + ~mpreal(); #ifdef MPREAL_HAVE_MOVE_SUPPORT - mpreal& operator=(mpreal&& v); - mpreal(mpreal&& u); + mpreal& operator=(mpreal&& v); + mpreal(mpreal&& u); #endif - // Operations - // = - // +, -, *, /, ++, --, <<, >> - // *=, +=, -=, /=, - // <, >, ==, <=, >= - - // = - mpreal& operator=(const mpreal& v); - mpreal& operator=(const mpf_t v); - mpreal& operator=(const mpz_t v); - mpreal& operator=(const mpq_t v); - mpreal& operator=(const long double v); - mpreal& operator=(const double v); - mpreal& operator=(const unsigned long int v); - mpreal& operator=(const unsigned long long int v); - mpreal& operator=(const long long int v); - mpreal& operator=(const unsigned int v); - mpreal& operator=(const long int v); - mpreal& operator=(const int v); - mpreal& operator=(const char* s); - mpreal& operator=(const std::string& s); - template mpreal& operator= (const std::complex& z); - - // + - mpreal& operator+=(const mpreal& v); - mpreal& operator+=(const mpf_t v); - mpreal& operator+=(const mpz_t v); - mpreal& operator+=(const mpq_t v); - mpreal& operator+=(const long double u); - mpreal& operator+=(const double u); - mpreal& operator+=(const unsigned long int u); - mpreal& operator+=(const unsigned int u); - mpreal& operator+=(const long int u); - mpreal& operator+=(const int u); - - mpreal& operator+=(const long long int u); - mpreal& operator+=(const unsigned long long int u); - mpreal& operator-=(const long long int u); - mpreal& operator-=(const unsigned long long int u); - mpreal& operator*=(const long long int u); - mpreal& operator*=(const unsigned long long int u); - mpreal& operator/=(const long long int u); - mpreal& operator/=(const unsigned long long int u); - - const mpreal operator+() const; - mpreal& operator++ (); - const mpreal operator++ (int); - - // - - mpreal& operator-=(const mpreal& v); - mpreal& operator-=(const mpz_t v); - mpreal& operator-=(const mpq_t v); - mpreal& operator-=(const long double u); - mpreal& operator-=(const double u); - mpreal& operator-=(const unsigned long int u); - mpreal& operator-=(const unsigned int u); - mpreal& operator-=(const long int u); - mpreal& operator-=(const int u); - const mpreal operator-() const; - friend const mpreal operator-(const unsigned long int b, const mpreal& a); - friend const mpreal operator-(const unsigned int b, const mpreal& a); - friend const mpreal operator-(const long int b, const mpreal& a); - friend const mpreal operator-(const int b, const mpreal& a); - friend const mpreal operator-(const double b, const mpreal& a); - mpreal& operator-- (); - const mpreal operator-- (int); - - // * - mpreal& operator*=(const mpreal& v); - mpreal& operator*=(const mpz_t v); - mpreal& operator*=(const mpq_t v); - mpreal& operator*=(const long double v); - mpreal& operator*=(const double v); - mpreal& operator*=(const unsigned long int v); - mpreal& operator*=(const unsigned int v); - mpreal& operator*=(const long int v); - mpreal& operator*=(const int v); - - // / - mpreal& operator/=(const mpreal& v); - mpreal& operator/=(const mpz_t v); - mpreal& operator/=(const mpq_t v); - mpreal& operator/=(const long double v); - mpreal& operator/=(const double v); - mpreal& operator/=(const unsigned long int v); - mpreal& operator/=(const unsigned int v); - mpreal& operator/=(const long int v); - mpreal& operator/=(const int v); - friend const mpreal operator/(const unsigned long int b, const mpreal& a); - friend const mpreal operator/(const unsigned int b, const mpreal& a); - friend const mpreal operator/(const long int b, const mpreal& a); - friend const mpreal operator/(const int b, const mpreal& a); - friend const mpreal operator/(const double b, const mpreal& a); - - //<<= Fast Multiplication by 2^u - mpreal& operator<<=(const unsigned long int u); - mpreal& operator<<=(const unsigned int u); - mpreal& operator<<=(const long int u); - mpreal& operator<<=(const int u); - - //>>= Fast Division by 2^u - mpreal& operator>>=(const unsigned long int u); - mpreal& operator>>=(const unsigned int u); - mpreal& operator>>=(const long int u); - mpreal& operator>>=(const int u); - - // Type Conversion operators - bool toBool ( ) const; - long toLong (mp_rnd_t mode = GMP_RNDZ) const; - unsigned long toULong (mp_rnd_t mode = GMP_RNDZ) const; - long long toLLong (mp_rnd_t mode = GMP_RNDZ) const; - unsigned long long toULLong (mp_rnd_t mode = GMP_RNDZ) const; - float toFloat (mp_rnd_t mode = GMP_RNDN) const; - double toDouble (mp_rnd_t mode = GMP_RNDN) const; - long double toLDouble (mp_rnd_t mode = GMP_RNDN) const; + // Operations + // = + // +, -, *, /, ++, --, <<, >> + // *=, +=, -=, /=, + // <, >, ==, <=, >= + + // = + mpreal& operator=(const mpreal& v); + mpreal& operator=(const mpf_t v); + mpreal& operator=(const mpz_t v); + mpreal& operator=(const mpq_t v); + mpreal& operator=(const long double v); + mpreal& operator=(const double v); + mpreal& operator=(const unsigned long int v); + mpreal& operator=(const unsigned long long int v); + mpreal& operator=(const long long int v); + mpreal& operator=(const unsigned int v); + mpreal& operator=(const long int v); + mpreal& operator=(const int v); + mpreal& operator=(const char* s); + mpreal& operator=(const std::string& s); + template mpreal& operator= (const std::complex& z); + + // + + mpreal& operator+=(const mpreal& v); + mpreal& operator+=(const mpf_t v); + mpreal& operator+=(const mpz_t v); + mpreal& operator+=(const mpq_t v); + mpreal& operator+=(const long double u); + mpreal& operator+=(const double u); + mpreal& operator+=(const unsigned long int u); + mpreal& operator+=(const unsigned int u); + mpreal& operator+=(const long int u); + mpreal& operator+=(const int u); + + mpreal& operator+=(const long long int u); + mpreal& operator+=(const unsigned long long int u); + mpreal& operator-=(const long long int u); + mpreal& operator-=(const unsigned long long int u); + mpreal& operator*=(const long long int u); + mpreal& operator*=(const unsigned long long int u); + mpreal& operator/=(const long long int u); + mpreal& operator/=(const unsigned long long int u); + + const mpreal operator+() const; + mpreal& operator++ (); + const mpreal operator++ (int); + + // - + mpreal& operator-=(const mpreal& v); + mpreal& operator-=(const mpz_t v); + mpreal& operator-=(const mpq_t v); + mpreal& operator-=(const long double u); + mpreal& operator-=(const double u); + mpreal& operator-=(const unsigned long int u); + mpreal& operator-=(const unsigned int u); + mpreal& operator-=(const long int u); + mpreal& operator-=(const int u); + const mpreal operator-() const; + friend const mpreal operator-(const unsigned long int b, const mpreal& a); + friend const mpreal operator-(const unsigned int b, const mpreal& a); + friend const mpreal operator-(const long int b, const mpreal& a); + friend const mpreal operator-(const int b, const mpreal& a); + friend const mpreal operator-(const double b, const mpreal& a); + mpreal& operator-- (); + const mpreal operator-- (int); + + // * + mpreal& operator*=(const mpreal& v); + mpreal& operator*=(const mpz_t v); + mpreal& operator*=(const mpq_t v); + mpreal& operator*=(const long double v); + mpreal& operator*=(const double v); + mpreal& operator*=(const unsigned long int v); + mpreal& operator*=(const unsigned int v); + mpreal& operator*=(const long int v); + mpreal& operator*=(const int v); + + // / + mpreal& operator/=(const mpreal& v); + mpreal& operator/=(const mpz_t v); + mpreal& operator/=(const mpq_t v); + mpreal& operator/=(const long double v); + mpreal& operator/=(const double v); + mpreal& operator/=(const unsigned long int v); + mpreal& operator/=(const unsigned int v); + mpreal& operator/=(const long int v); + mpreal& operator/=(const int v); + friend const mpreal operator/(const unsigned long int b, const mpreal& a); + friend const mpreal operator/(const unsigned int b, const mpreal& a); + friend const mpreal operator/(const long int b, const mpreal& a); + friend const mpreal operator/(const int b, const mpreal& a); + friend const mpreal operator/(const double b, const mpreal& a); + + //<<= Fast Multiplication by 2^u + mpreal& operator<<=(const unsigned long int u); + mpreal& operator<<=(const unsigned int u); + mpreal& operator<<=(const long int u); + mpreal& operator<<=(const int u); + + //>>= Fast Division by 2^u + mpreal& operator>>=(const unsigned long int u); + mpreal& operator>>=(const unsigned int u); + mpreal& operator>>=(const long int u); + mpreal& operator>>=(const int u); + + // Type Conversion operators + bool toBool ( ) const; + long toLong (mp_rnd_t mode = GMP_RNDZ) const; + unsigned long toULong (mp_rnd_t mode = GMP_RNDZ) const; + long long toLLong (mp_rnd_t mode = GMP_RNDZ) const; + unsigned long long toULLong (mp_rnd_t mode = GMP_RNDZ) const; + float toFloat (mp_rnd_t mode = GMP_RNDN) const; + double toDouble (mp_rnd_t mode = GMP_RNDN) const; + long double toLDouble (mp_rnd_t mode = GMP_RNDN) const; #if defined (MPREAL_HAVE_EXPLICIT_CONVERTERS) - explicit operator bool () const { return toBool(); } - explicit operator int () const { return int(toLong()); } - explicit operator long () const { return toLong(); } - explicit operator long long () const { return toLLong(); } - explicit operator unsigned () const { return unsigned(toULong()); } - explicit operator unsigned long () const { return toULong(); } - explicit operator unsigned long long () const { return toULLong(); } - explicit operator float () const { return toFloat(); } - explicit operator double () const { return toDouble(); } - explicit operator long double () const { return toLDouble(); } + explicit operator bool () const { return toBool(); } + explicit operator int () const { return int(toLong()); } + explicit operator long () const { return toLong(); } + explicit operator long long () const { return toLLong(); } + explicit operator unsigned () const { return unsigned(toULong()); } + explicit operator unsigned long () const { return toULong(); } + explicit operator unsigned long long () const { return toULLong(); } + explicit operator float () const { return toFloat(); } + explicit operator double () const { return toDouble(); } + explicit operator long double () const { return toLDouble(); } #endif - // Get raw pointers so that mpreal can be directly used in raw mpfr_* functions - ::mpfr_ptr mpfr_ptr(); - ::mpfr_srcptr mpfr_ptr() const; - ::mpfr_srcptr mpfr_srcptr() const; + // Get raw pointers so that mpreal can be directly used in raw mpfr_* functions + ::mpfr_ptr mpfr_ptr(); + ::mpfr_srcptr mpfr_ptr() const; + ::mpfr_srcptr mpfr_srcptr() const; - // Convert mpreal to string with n significant digits in base b - // n = -1 -> convert with the maximum available digits - std::string toString(int n = -1, int b = 10, mp_rnd_t mode = mpreal::get_default_rnd()) const; + // Convert mpreal to string with n significant digits in base b + // n = -1 -> convert with the maximum available digits + std::string toString(int n = -1, int b = 10, mp_rnd_t mode = mpreal::get_default_rnd()) const; #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - std::string toString(const std::string& format) const; + std::string toString(const std::string& format) const; #endif - std::ostream& output(std::ostream& os) const; - - // Math Functions - friend const mpreal sqr (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal sqrt(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal sqrt(const unsigned long int v, mp_rnd_t rnd_mode); - friend const mpreal cbrt(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal root(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode); - friend const mpreal pow (const mpreal& a, const mpreal& b, mp_rnd_t rnd_mode); - friend const mpreal pow (const mpreal& a, const mpz_t b, mp_rnd_t rnd_mode); - friend const mpreal pow (const mpreal& a, const unsigned long int b, mp_rnd_t rnd_mode); - friend const mpreal pow (const mpreal& a, const long int b, mp_rnd_t rnd_mode); - friend const mpreal pow (const unsigned long int a, const mpreal& b, mp_rnd_t rnd_mode); - friend const mpreal pow (const unsigned long int a, const unsigned long int b, mp_rnd_t rnd_mode); - friend const mpreal fabs(const mpreal& v, mp_rnd_t rnd_mode); - - friend const mpreal abs(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal dim(const mpreal& a, const mpreal& b, mp_rnd_t rnd_mode); - friend inline const mpreal mul_2ui(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode); - friend inline const mpreal mul_2si(const mpreal& v, long int k, mp_rnd_t rnd_mode); - friend inline const mpreal div_2ui(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode); - friend inline const mpreal div_2si(const mpreal& v, long int k, mp_rnd_t rnd_mode); - friend int cmpabs(const mpreal& a,const mpreal& b); - - friend const mpreal log (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal log2 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal logb (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal log10(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal exp (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal exp2 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal exp10(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal log1p(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal expm1(const mpreal& v, mp_rnd_t rnd_mode); - - friend const mpreal cos(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal sin(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal tan(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal sec(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal csc(const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal cot(const mpreal& v, mp_rnd_t rnd_mode); - friend int sin_cos(mpreal& s, mpreal& c, const mpreal& v, mp_rnd_t rnd_mode); - - friend const mpreal acos (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal asin (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal atan (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal atan2 (const mpreal& y, const mpreal& x, mp_rnd_t rnd_mode); - friend const mpreal acot (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal asec (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal acsc (const mpreal& v, mp_rnd_t rnd_mode); - - friend const mpreal cosh (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal sinh (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal tanh (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal sech (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal csch (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal coth (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal acosh (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal asinh (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal atanh (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal acoth (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal asech (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal acsch (const mpreal& v, mp_rnd_t rnd_mode); - - friend const mpreal hypot (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); - - friend const mpreal fac_ui (unsigned long int v, mp_prec_t prec, mp_rnd_t rnd_mode); - friend const mpreal eint (const mpreal& v, mp_rnd_t rnd_mode); - - friend const mpreal gamma (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal tgamma (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal lngamma (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal lgamma (const mpreal& v, int *signp, mp_rnd_t rnd_mode); - friend const mpreal zeta (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal erf (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal erfc (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal besselj0 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal besselj1 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal besseljn (long n, const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal bessely0 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal bessely1 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal besselyn (long n, const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal fma (const mpreal& v1, const mpreal& v2, const mpreal& v3, mp_rnd_t rnd_mode); - friend const mpreal fms (const mpreal& v1, const mpreal& v2, const mpreal& v3, mp_rnd_t rnd_mode); - friend const mpreal agm (const mpreal& v1, const mpreal& v2, mp_rnd_t rnd_mode); - friend const mpreal sum (const mpreal tab[], const unsigned long int n, int& status, mp_rnd_t rnd_mode); - friend int sgn(const mpreal& v); // returns -1 or +1 + std::ostream& output(std::ostream& os) const; + + // Math Functions + friend const mpreal sqr (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal sqrt(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal sqrt(const unsigned long int v, mp_rnd_t rnd_mode); + friend const mpreal cbrt(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal root(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode); + friend const mpreal pow (const mpreal& a, const mpreal& b, mp_rnd_t rnd_mode); + friend const mpreal pow (const mpreal& a, const mpz_t b, mp_rnd_t rnd_mode); + friend const mpreal pow (const mpreal& a, const unsigned long int b, mp_rnd_t rnd_mode); + friend const mpreal pow (const mpreal& a, const long int b, mp_rnd_t rnd_mode); + friend const mpreal pow (const unsigned long int a, const mpreal& b, mp_rnd_t rnd_mode); + friend const mpreal pow (const unsigned long int a, const unsigned long int b, mp_rnd_t rnd_mode); + friend const mpreal fabs(const mpreal& v, mp_rnd_t rnd_mode); + + friend const mpreal abs(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal dim(const mpreal& a, const mpreal& b, mp_rnd_t rnd_mode); + friend inline const mpreal mul_2ui(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode); + friend inline const mpreal mul_2si(const mpreal& v, long int k, mp_rnd_t rnd_mode); + friend inline const mpreal div_2ui(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode); + friend inline const mpreal div_2si(const mpreal& v, long int k, mp_rnd_t rnd_mode); + friend int cmpabs(const mpreal& a,const mpreal& b); + + friend const mpreal log (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal log2 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal logb (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal log10(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal exp (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal exp2 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal exp10(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal log1p(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal expm1(const mpreal& v, mp_rnd_t rnd_mode); + + friend const mpreal cos(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal sin(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal tan(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal sec(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal csc(const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal cot(const mpreal& v, mp_rnd_t rnd_mode); + friend int sin_cos(mpreal& s, mpreal& c, const mpreal& v, mp_rnd_t rnd_mode); + + friend const mpreal acos (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal asin (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal atan (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal atan2 (const mpreal& y, const mpreal& x, mp_rnd_t rnd_mode); + friend const mpreal acot (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal asec (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal acsc (const mpreal& v, mp_rnd_t rnd_mode); + + friend const mpreal cosh (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal sinh (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal tanh (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal sech (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal csch (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal coth (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal acosh (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal asinh (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal atanh (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal acoth (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal asech (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal acsch (const mpreal& v, mp_rnd_t rnd_mode); + + friend const mpreal hypot (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); + + friend const mpreal fac_ui (unsigned long int v, mp_prec_t prec, mp_rnd_t rnd_mode); + friend const mpreal eint (const mpreal& v, mp_rnd_t rnd_mode); + + friend const mpreal gamma (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal tgamma (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal lngamma (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal lgamma (const mpreal& v, int *signp, mp_rnd_t rnd_mode); + friend const mpreal zeta (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal erf (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal erfc (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal besselj0 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal besselj1 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal besseljn (long n, const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal bessely0 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal bessely1 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal besselyn (long n, const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal fma (const mpreal& v1, const mpreal& v2, const mpreal& v3, mp_rnd_t rnd_mode); + friend const mpreal fms (const mpreal& v1, const mpreal& v2, const mpreal& v3, mp_rnd_t rnd_mode); + friend const mpreal agm (const mpreal& v1, const mpreal& v2, mp_rnd_t rnd_mode); + friend const mpreal sum (const mpreal tab[], const unsigned long int n, int& status, mp_rnd_t rnd_mode); + friend int sgn(const mpreal& v); // returns -1 or +1 // MPFR 2.4.0 Specifics #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - friend int sinh_cosh (mpreal& s, mpreal& c, const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal li2 (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal fmod (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); - friend const mpreal rec_sqrt (const mpreal& v, mp_rnd_t rnd_mode); - - // MATLAB's semantic equivalents - friend const mpreal rem (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); // Remainder after division - friend const mpreal mod (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); // Modulus after division + friend int sinh_cosh (mpreal& s, mpreal& c, const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal li2 (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal fmod (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); + friend const mpreal rec_sqrt (const mpreal& v, mp_rnd_t rnd_mode); + + // MATLAB's semantic equivalents + friend const mpreal rem (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); // Remainder after division + friend const mpreal mod (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); // Modulus after division #endif #if (MPFR_VERSION >= MPFR_VERSION_NUM(3,0,0)) - friend const mpreal digamma (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal ai (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal urandom (gmp_randstate_t& state, mp_rnd_t rnd_mode); // use gmp_randinit_default() to init state, gmp_randclear() to clear + friend const mpreal digamma (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal ai (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal urandom (gmp_randstate_t& state, mp_rnd_t rnd_mode); // use gmp_randinit_default() to init state, gmp_randclear() to clear #endif #if (MPFR_VERSION >= MPFR_VERSION_NUM(3,1,0)) - friend const mpreal grandom (gmp_randstate_t& state, mp_rnd_t rnd_mode); // use gmp_randinit_default() to init state, gmp_randclear() to clear - friend const mpreal grandom (unsigned int seed); + friend const mpreal grandom (gmp_randstate_t& state, mp_rnd_t rnd_mode); // use gmp_randinit_default() to init state, gmp_randclear() to clear + friend const mpreal grandom (unsigned int seed); #endif - // Uniformly distributed random number generation in [0,1] using - // Mersenne-Twister algorithm by default. - // Use parameter to setup seed, e.g.: random((unsigned)time(NULL)) - // Check urandom() for more precise control. - friend const mpreal random(unsigned int seed); - - // Exponent and mantissa manipulation - friend const mpreal frexp (const mpreal& v, mp_exp_t* exp); - friend const mpreal ldexp (const mpreal& v, mp_exp_t exp); - friend const mpreal scalbn(const mpreal& v, mp_exp_t exp); - - // Splits mpreal value into fractional and integer parts. - // Returns fractional part and stores integer part in n. - friend const mpreal modf(const mpreal& v, mpreal& n); - - // Constants - // don't forget to call mpfr_free_cache() for every thread where you are using const-functions - friend const mpreal const_log2 (mp_prec_t prec, mp_rnd_t rnd_mode); - friend const mpreal const_pi (mp_prec_t prec, mp_rnd_t rnd_mode); - friend const mpreal const_euler (mp_prec_t prec, mp_rnd_t rnd_mode); - friend const mpreal const_catalan (mp_prec_t prec, mp_rnd_t rnd_mode); - - // returns +inf iff sign>=0 otherwise -inf - friend const mpreal const_infinity(int sign, mp_prec_t prec); - - // Output/ Input - friend std::ostream& operator<<(std::ostream& os, const mpreal& v); - friend std::istream& operator>>(std::istream& is, mpreal& v); - - // Integer Related Functions - friend const mpreal rint (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal ceil (const mpreal& v); - friend const mpreal floor(const mpreal& v); - friend const mpreal round(const mpreal& v); - friend const mpreal trunc(const mpreal& v); - friend const mpreal rint_ceil (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal rint_floor (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal rint_round (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal rint_trunc (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal frac (const mpreal& v, mp_rnd_t rnd_mode); - friend const mpreal remainder ( const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); - friend const mpreal remquo (long* q, const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); - - // Miscellaneous Functions - friend const mpreal nexttoward (const mpreal& x, const mpreal& y); - friend const mpreal nextabove (const mpreal& x); - friend const mpreal nextbelow (const mpreal& x); - - // use gmp_randinit_default() to init state, gmp_randclear() to clear - friend const mpreal urandomb (gmp_randstate_t& state); + // Uniformly distributed random number generation in [0,1] using + // Mersenne-Twister algorithm by default. + // Use parameter to setup seed, e.g.: random((unsigned)time(NULL)) + // Check urandom() for more precise control. + friend const mpreal random(unsigned int seed); + + // Exponent and mantissa manipulation + friend const mpreal frexp (const mpreal& v, mp_exp_t* exp); + friend const mpreal ldexp (const mpreal& v, mp_exp_t exp); + friend const mpreal scalbn(const mpreal& v, mp_exp_t exp); + + // Splits mpreal value into fractional and integer parts. + // Returns fractional part and stores integer part in n. + friend const mpreal modf(const mpreal& v, mpreal& n); + + // Constants + // don't forget to call mpfr_free_cache() for every thread where you are using const-functions + friend const mpreal const_log2 (mp_prec_t prec, mp_rnd_t rnd_mode); + friend const mpreal const_pi (mp_prec_t prec, mp_rnd_t rnd_mode); + friend const mpreal const_euler (mp_prec_t prec, mp_rnd_t rnd_mode); + friend const mpreal const_catalan (mp_prec_t prec, mp_rnd_t rnd_mode); + + // returns +inf iff sign>=0 otherwise -inf + friend const mpreal const_infinity(int sign, mp_prec_t prec); + + // Output/ Input + friend std::ostream& operator<<(std::ostream& os, const mpreal& v); + friend std::istream& operator>>(std::istream& is, mpreal& v); + + // Integer Related Functions + friend const mpreal rint (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal ceil (const mpreal& v); + friend const mpreal floor(const mpreal& v); + friend const mpreal round(const mpreal& v); + friend const mpreal trunc(const mpreal& v); + friend const mpreal rint_ceil (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal rint_floor (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal rint_round (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal rint_trunc (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal frac (const mpreal& v, mp_rnd_t rnd_mode); + friend const mpreal remainder ( const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); + friend const mpreal remquo (long* q, const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); + + // Miscellaneous Functions + friend const mpreal nexttoward (const mpreal& x, const mpreal& y); + friend const mpreal nextabove (const mpreal& x); + friend const mpreal nextbelow (const mpreal& x); + + // use gmp_randinit_default() to init state, gmp_randclear() to clear + friend const mpreal urandomb (gmp_randstate_t& state); // MPFR < 2.4.2 Specifics #if (MPFR_VERSION <= MPFR_VERSION_NUM(2,4,2)) - friend const mpreal random2 (mp_size_t size, mp_exp_t exp); + friend const mpreal random2 (mp_size_t size, mp_exp_t exp); #endif - // Instance Checkers - friend bool isnan (const mpreal& v); - friend bool isinf (const mpreal& v); - friend bool isfinite (const mpreal& v); + // Instance Checkers + friend bool isnan (const mpreal& v); + friend bool isinf (const mpreal& v); + friend bool isfinite (const mpreal& v); - friend bool isnum (const mpreal& v); - friend bool iszero (const mpreal& v); - friend bool isint (const mpreal& v); + friend bool isnum (const mpreal& v); + friend bool iszero (const mpreal& v); + friend bool isint (const mpreal& v); #if (MPFR_VERSION >= MPFR_VERSION_NUM(3,0,0)) - friend bool isregular(const mpreal& v); + friend bool isregular(const mpreal& v); #endif - // Set/Get instance properties - inline mp_prec_t get_prec() const; - inline void set_prec(mp_prec_t prec, mp_rnd_t rnd_mode = get_default_rnd()); // Change precision with rounding mode + // Set/Get instance properties + inline mp_prec_t get_prec() const; + inline void set_prec(mp_prec_t prec, mp_rnd_t rnd_mode = get_default_rnd()); // Change precision with rounding mode - // Aliases for get_prec(), set_prec() - needed for compatibility with std::complex interface - inline mpreal& setPrecision(int Precision, mp_rnd_t RoundingMode = get_default_rnd()); - inline int getPrecision() const; + // Aliases for get_prec(), set_prec() - needed for compatibility with std::complex interface + inline mpreal& setPrecision(int Precision, mp_rnd_t RoundingMode = get_default_rnd()); + inline int getPrecision() const; - // Set mpreal to +/- inf, NaN, +/-0 - mpreal& setInf (int Sign = +1); - mpreal& setNan (); - mpreal& setZero (int Sign = +1); - mpreal& setSign (int Sign, mp_rnd_t RoundingMode = get_default_rnd()); + // Set mpreal to +/- inf, NaN, +/-0 + mpreal& setInf (int Sign = +1); + mpreal& setNan (); + mpreal& setZero (int Sign = +1); + mpreal& setSign (int Sign, mp_rnd_t RoundingMode = get_default_rnd()); - //Exponent - mp_exp_t get_exp(); - int set_exp(mp_exp_t e); - int check_range (int t, mp_rnd_t rnd_mode = get_default_rnd()); - int subnormalize (int t, mp_rnd_t rnd_mode = get_default_rnd()); + //Exponent + mp_exp_t get_exp(); + int set_exp(mp_exp_t e); + int check_range (int t, mp_rnd_t rnd_mode = get_default_rnd()); + int subnormalize (int t, mp_rnd_t rnd_mode = get_default_rnd()); - // Inexact conversion from float - inline bool fits_in_bits(double x, int n); + // Inexact conversion from float + inline bool fits_in_bits(double x, int n); - // Set/Get global properties - static void set_default_prec(mp_prec_t prec); - static void set_default_rnd(mp_rnd_t rnd_mode); + // Set/Get global properties + static void set_default_prec(mp_prec_t prec); + static void set_default_rnd(mp_rnd_t rnd_mode); - static mp_exp_t get_emin (void); - static mp_exp_t get_emax (void); - static mp_exp_t get_emin_min (void); - static mp_exp_t get_emin_max (void); - static mp_exp_t get_emax_min (void); - static mp_exp_t get_emax_max (void); - static int set_emin (mp_exp_t exp); - static int set_emax (mp_exp_t exp); + static mp_exp_t get_emin (void); + static mp_exp_t get_emax (void); + static mp_exp_t get_emin_min (void); + static mp_exp_t get_emin_max (void); + static mp_exp_t get_emax_min (void); + static mp_exp_t get_emax_max (void); + static int set_emin (mp_exp_t exp); + static int set_emax (mp_exp_t exp); - // Efficient swapping of two mpreal values - needed for std algorithms - friend void swap(mpreal& x, mpreal& y); + // Efficient swapping of two mpreal values - needed for std algorithms + friend void swap(mpreal& x, mpreal& y); - friend const mpreal fmax(const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); - friend const mpreal fmin(const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); + friend const mpreal fmax(const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); + friend const mpreal fmin(const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode); private: - // Human friendly Debug Preview in Visual Studio. - // Put one of these lines: - // - // mpfr::mpreal= ; Show value only - // mpfr::mpreal=, bits ; Show value & precision - // - // at the beginning of - // [Visual Studio Installation Folder]\Common7\Packages\Debugger\autoexp.dat - MPREAL_MSVC_DEBUGVIEW_DATA - - // "Smart" resources deallocation. Checks if instance initialized before deletion. - void clear(::mpfr_ptr); + // Human friendly Debug Preview in Visual Studio. + // Put one of these lines: + // + // mpfr::mpreal= ; Show value only + // mpfr::mpreal=, bits ; Show value & precision + // + // at the beginning of + // [Visual Studio Installation Folder]\Common7\Packages\Debugger\autoexp.dat + MPREAL_MSVC_DEBUGVIEW_DATA + + // "Smart" resources deallocation. Checks if instance initialized before deletion. + void clear(::mpfr_ptr); }; ////////////////////////////////////////////////////////////////////////// // Exceptions class conversion_overflow : public std::exception { public: - std::string why() { return "inexact conversion from floating point"; } + std::string why() { return "inexact conversion from floating point"; } }; ////////////////////////////////////////////////////////////////////////// @@ -590,80 +588,80 @@ class conversion_overflow : public std::exception { // Default constructor: creates mp number and initializes it to 0. inline mpreal::mpreal() { - mpfr_init2(mpfr_ptr(), mpreal::get_default_prec()); - mpfr_set_zero_fast(mpfr_ptr()); + mpfr_init2(mpfr_ptr(), mpreal::get_default_prec()); + mpfr_set_zero_fast(mpfr_ptr()); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const mpreal& u) { - mpfr_init2(mpfr_ptr(),mpfr_get_prec(u.mpfr_srcptr())); - mpfr_set (mpfr_ptr(),u.mpfr_srcptr(),mpreal::get_default_rnd()); + mpfr_init2(mpfr_ptr(),mpfr_get_prec(u.mpfr_srcptr())); + mpfr_set (mpfr_ptr(),u.mpfr_srcptr(),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } #ifdef MPREAL_HAVE_MOVE_SUPPORT inline mpreal::mpreal(mpreal&& other) { - mpfr_set_uninitialized(mpfr_ptr()); // make sure "other" holds no pointer to actual data - mpfr_swap(mpfr_ptr(), other.mpfr_ptr()); + mpfr_set_uninitialized(mpfr_ptr()); // make sure "other" holds no pointer to actual data + mpfr_swap(mpfr_ptr(), other.mpfr_ptr()); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal& mpreal::operator=(mpreal&& other) { - mpfr_swap(mpfr_ptr(), other.mpfr_ptr()); + mpfr_swap(mpfr_ptr(), other.mpfr_ptr()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } #endif inline mpreal::mpreal(const mpfr_t u, bool shared) { - if(shared) - { - std::memcpy(mpfr_ptr(), u, sizeof(mpfr_t)); - } - else - { - mpfr_init2(mpfr_ptr(), mpfr_get_prec(u)); - mpfr_set (mpfr_ptr(), u, mpreal::get_default_rnd()); - } + if(shared) + { + std::memcpy(mpfr_ptr(), u, sizeof(mpfr_t)); + } + else + { + mpfr_init2(mpfr_ptr(), mpfr_get_prec(u)); + mpfr_set (mpfr_ptr(), u, mpreal::get_default_rnd()); + } - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const mpf_t u) { - mpfr_init2(mpfr_ptr(),(mp_prec_t) mpf_get_prec(u)); // (gmp: mp_bitcnt_t) unsigned long -> long (mpfr: mp_prec_t) - mpfr_set_f(mpfr_ptr(),u,mpreal::get_default_rnd()); + mpfr_init2(mpfr_ptr(),(mp_prec_t) mpf_get_prec(u)); // (gmp: mp_bitcnt_t) unsigned long -> long (mpfr: mp_prec_t) + mpfr_set_f(mpfr_ptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const mpz_t u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2(mpfr_ptr(), prec); - mpfr_set_z(mpfr_ptr(), u, mode); + mpfr_init2(mpfr_ptr(), prec); + mpfr_set_z(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const mpq_t u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2(mpfr_ptr(), prec); - mpfr_set_q(mpfr_ptr(), u, mode); + mpfr_init2(mpfr_ptr(), prec); + mpfr_set_q(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const double u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2(mpfr_ptr(), prec); + mpfr_init2(mpfr_ptr(), prec); #if (MPREAL_DOUBLE_BITS_OVERFLOW > -1) if(fits_in_bits(u, MPREAL_DOUBLE_BITS_OVERFLOW)) @@ -675,150 +673,150 @@ inline mpreal::mpreal(const double u, mp_prec_t prec, mp_rnd_t mode) mpfr_set_d(mpfr_ptr(), u, mode); #endif - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const long double u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_ld(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_ld(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const unsigned long long int u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_uj(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_uj(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const long long int u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_sj(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_sj(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const unsigned long int u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_ui(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_ui(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const unsigned int u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_ui(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_ui(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const long int u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_si(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_si(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const int u, mp_prec_t prec, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_si(mpfr_ptr(), u, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_si(mpfr_ptr(), u, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const char* s, mp_prec_t prec, int base, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_str(mpfr_ptr(), s, base, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_str(mpfr_ptr(), s, base, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mpreal::mpreal(const std::string& s, mp_prec_t prec, int base, mp_rnd_t mode) { - mpfr_init2 (mpfr_ptr(), prec); - mpfr_set_str(mpfr_ptr(), s.c_str(), base, mode); + mpfr_init2 (mpfr_ptr(), prec); + mpfr_set_str(mpfr_ptr(), s.c_str(), base, mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + MPREAL_MSVC_DEBUGVIEW_CODE; } inline void mpreal::clear(::mpfr_ptr x) { #ifdef MPREAL_HAVE_MOVE_SUPPORT - if(mpfr_is_initialized(x)) + if(mpfr_is_initialized(x)) #endif - mpfr_clear(x); + mpfr_clear(x); } inline mpreal::~mpreal() { - clear(mpfr_ptr()); + clear(mpfr_ptr()); } // internal namespace needed for template magic namespace internal{ - // Use SFINAE to restrict arithmetic operations instantiation only for numeric types - // This is needed for smooth integration with libraries based on expression templates, like Eigen. - // TODO: Do the same for boolean operators. - template struct result_type {}; + // Use SFINAE to restrict arithmetic operations instantiation only for numeric types + // This is needed for smooth integration with libraries based on expression templates, like Eigen. + // TODO: Do the same for boolean operators. + template struct result_type {}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; - template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; + template <> struct result_type {typedef mpreal type;}; } // + Addition template inline const typename internal::result_type::type - operator+(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) += rhs; } + operator+(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) += rhs; } template inline const typename internal::result_type::type - operator+(const Lhs& lhs, const mpreal& rhs){ return mpreal(rhs) += lhs; } + operator+(const Lhs& lhs, const mpreal& rhs){ return mpreal(rhs) += lhs; } // - Subtraction template inline const typename internal::result_type::type - operator-(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) -= rhs; } + operator-(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) -= rhs; } template inline const typename internal::result_type::type - operator-(const Lhs& lhs, const mpreal& rhs){ return mpreal(lhs) -= rhs; } + operator-(const Lhs& lhs, const mpreal& rhs){ return mpreal(lhs) -= rhs; } // * Multiplication template inline const typename internal::result_type::type - operator*(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) *= rhs; } + operator*(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) *= rhs; } template inline const typename internal::result_type::type - operator*(const Lhs& lhs, const mpreal& rhs){ return mpreal(rhs) *= lhs; } + operator*(const Lhs& lhs, const mpreal& rhs){ return mpreal(rhs) *= lhs; } // / Division template inline const typename internal::result_type::type - operator/(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) /= rhs; } + operator/(const mpreal& lhs, const Rhs& rhs){ return mpreal(lhs) /= rhs; } template inline const typename internal::result_type::type - operator/(const Lhs& lhs, const mpreal& rhs){ return mpreal(lhs) /= rhs; } + operator/(const Lhs& lhs, const mpreal& rhs){ return mpreal(lhs) /= rhs; } ////////////////////////////////////////////////////////////////////////// // sqrt @@ -933,8 +931,8 @@ const mpreal (min)(const mpreal& x, const mpreal& y); // Operators - Assignment inline mpreal& mpreal::operator=(const mpreal& v) { - if (this != &v) - { + if (this != &v) + { mp_prec_t tp = mpfr_get_prec( mpfr_srcptr()); mp_prec_t vp = mpfr_get_prec(v.mpfr_srcptr()); @@ -943,43 +941,43 @@ inline mpreal& mpreal::operator=(const mpreal& v) mpfr_init2(mpfr_ptr(), vp); } - mpfr_set(mpfr_ptr(), v.mpfr_srcptr(), mpreal::get_default_rnd()); + mpfr_set(mpfr_ptr(), v.mpfr_srcptr(), mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - } - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + } + return *this; } inline mpreal& mpreal::operator=(const mpf_t v) { - mpfr_set_f(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_f(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const mpz_t v) { - mpfr_set_z(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_z(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const mpq_t v) { - mpfr_set_q(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_q(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const long double v) { - mpfr_set_ld(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_ld(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const double v) @@ -995,184 +993,184 @@ inline mpreal& mpreal::operator=(const double v) #endif MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + return *this; } inline mpreal& mpreal::operator=(const unsigned long int v) { - mpfr_set_ui(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_ui(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const unsigned int v) { - mpfr_set_ui(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_ui(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const unsigned long long int v) { - mpfr_set_uj(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_uj(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const long long int v) { - mpfr_set_sj(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_sj(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const long int v) { - mpfr_set_si(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_si(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const int v) { - mpfr_set_si(mpfr_ptr(), v, mpreal::get_default_rnd()); + mpfr_set_si(mpfr_ptr(), v, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator=(const char* s) { - // Use other converters for more precise control on base & precision & rounding: - // - // mpreal(const char* s, mp_prec_t prec, int base, mp_rnd_t mode) - // mpreal(const std::string& s,mp_prec_t prec, int base, mp_rnd_t mode) - // - // Here we assume base = 10 and we use precision of target variable. + // Use other converters for more precise control on base & precision & rounding: + // + // mpreal(const char* s, mp_prec_t prec, int base, mp_rnd_t mode) + // mpreal(const std::string& s,mp_prec_t prec, int base, mp_rnd_t mode) + // + // Here we assume base = 10 and we use precision of target variable. - mpfr_t t; + mpfr_t t; - mpfr_init2(t, mpfr_get_prec(mpfr_srcptr())); + mpfr_init2(t, mpfr_get_prec(mpfr_srcptr())); - if(0 == mpfr_set_str(t, s, 10, mpreal::get_default_rnd())) - { - mpfr_set(mpfr_ptr(), t, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - } + if(0 == mpfr_set_str(t, s, 10, mpreal::get_default_rnd())) + { + mpfr_set(mpfr_ptr(), t, mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + } - clear(t); - return *this; + clear(t); + return *this; } inline mpreal& mpreal::operator=(const std::string& s) { - // Use other converters for more precise control on base & precision & rounding: - // - // mpreal(const char* s, mp_prec_t prec, int base, mp_rnd_t mode) - // mpreal(const std::string& s,mp_prec_t prec, int base, mp_rnd_t mode) - // - // Here we assume base = 10 and we use precision of target variable. + // Use other converters for more precise control on base & precision & rounding: + // + // mpreal(const char* s, mp_prec_t prec, int base, mp_rnd_t mode) + // mpreal(const std::string& s,mp_prec_t prec, int base, mp_rnd_t mode) + // + // Here we assume base = 10 and we use precision of target variable. - mpfr_t t; + mpfr_t t; - mpfr_init2(t, mpfr_get_prec(mpfr_srcptr())); + mpfr_init2(t, mpfr_get_prec(mpfr_srcptr())); - if(0 == mpfr_set_str(t, s.c_str(), 10, mpreal::get_default_rnd())) - { - mpfr_set(mpfr_ptr(), t, mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - } + if(0 == mpfr_set_str(t, s.c_str(), 10, mpreal::get_default_rnd())) + { + mpfr_set(mpfr_ptr(), t, mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + } - clear(t); - return *this; + clear(t); + return *this; } template inline mpreal& mpreal::operator= (const std::complex& z) { - return *this = z.real(); + return *this = z.real(); } ////////////////////////////////////////////////////////////////////////// // + Addition inline mpreal& mpreal::operator+=(const mpreal& v) { - mpfr_add(mpfr_ptr(), mpfr_srcptr(), v.mpfr_srcptr(), mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add(mpfr_ptr(), mpfr_srcptr(), v.mpfr_srcptr(), mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const mpf_t u) { - *this += mpreal(u); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + *this += mpreal(u); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const mpz_t u) { - mpfr_add_z(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add_z(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const mpq_t u) { - mpfr_add_q(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add_q(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+= (const long double u) { - *this += mpreal(u); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + *this += mpreal(u); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+= (const double u) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - mpfr_add_d(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + mpfr_add_d(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); #else - *this += mpreal(u); + *this += mpreal(u); #endif - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const unsigned long int u) { - mpfr_add_ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add_ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const unsigned int u) { - mpfr_add_ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add_ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const long int u) { - mpfr_add_si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add_si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const int u) { - mpfr_add_si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_add_si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator+=(const long long int u) { *this += mpreal(u); MPREAL_MSVC_DEBUGVIEW_CODE; return *this; } @@ -1195,103 +1193,103 @@ inline const mpreal operator+(const mpreal& a, const mpreal& b) inline mpreal& mpreal::operator++() { - return *this += 1; + return *this += 1; } inline const mpreal mpreal::operator++ (int) { - mpreal x(*this); - *this += 1; - return x; + mpreal x(*this); + *this += 1; + return x; } inline mpreal& mpreal::operator--() { - return *this -= 1; + return *this -= 1; } inline const mpreal mpreal::operator-- (int) { - mpreal x(*this); - *this -= 1; - return x; + mpreal x(*this); + *this -= 1; + return x; } ////////////////////////////////////////////////////////////////////////// // - Subtraction inline mpreal& mpreal::operator-=(const mpreal& v) { - mpfr_sub(mpfr_ptr(),mpfr_srcptr(),v.mpfr_srcptr(),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub(mpfr_ptr(),mpfr_srcptr(),v.mpfr_srcptr(),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const mpz_t v) { - mpfr_sub_z(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub_z(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const mpq_t v) { - mpfr_sub_q(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub_q(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const long double v) { - *this -= mpreal(v); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + *this -= mpreal(v); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const double v) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - mpfr_sub_d(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + mpfr_sub_d(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); #else - *this -= mpreal(v); + *this -= mpreal(v); #endif - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const unsigned long int v) { - mpfr_sub_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const unsigned int v) { - mpfr_sub_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const long int v) { - mpfr_sub_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator-=(const int v) { - mpfr_sub_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_sub_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline const mpreal mpreal::operator-()const { - mpreal u(*this); - mpfr_neg(u.mpfr_ptr(),u.mpfr_srcptr(),mpreal::get_default_rnd()); - return u; + mpreal u(*this); + mpfr_neg(u.mpfr_ptr(),u.mpfr_srcptr(),mpreal::get_default_rnd()); + return u; } inline const mpreal operator-(const mpreal& a, const mpreal& b) @@ -1304,111 +1302,111 @@ inline const mpreal operator-(const mpreal& a, const mpreal& b) inline const mpreal operator-(const double b, const mpreal& a) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); - mpfr_d_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); + mpfr_d_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; #else - mpreal x(b, mpfr_get_prec(a.mpfr_ptr())); - x -= a; - return x; + mpreal x(b, mpfr_get_prec(a.mpfr_ptr())); + x -= a; + return x; #endif } inline const mpreal operator-(const unsigned long int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); - mpfr_ui_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); + mpfr_ui_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator-(const unsigned int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); - mpfr_ui_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); + mpfr_ui_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator-(const long int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); - mpfr_si_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); + mpfr_si_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator-(const int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); - mpfr_si_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); + mpfr_si_sub(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } ////////////////////////////////////////////////////////////////////////// // * Multiplication inline mpreal& mpreal::operator*= (const mpreal& v) { - mpfr_mul(mpfr_ptr(),mpfr_srcptr(),v.mpfr_srcptr(),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul(mpfr_ptr(),mpfr_srcptr(),v.mpfr_srcptr(),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const mpz_t v) { - mpfr_mul_z(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_z(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const mpq_t v) { - mpfr_mul_q(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_q(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const long double v) { - *this *= mpreal(v); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + *this *= mpreal(v); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const double v) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - mpfr_mul_d(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + mpfr_mul_d(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); #else - *this *= mpreal(v); + *this *= mpreal(v); #endif - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const unsigned long int v) { - mpfr_mul_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const unsigned int v) { - mpfr_mul_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const long int v) { - mpfr_mul_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator*=(const int v) { - mpfr_mul_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline const mpreal operator*(const mpreal& a, const mpreal& b) @@ -1422,69 +1420,69 @@ inline const mpreal operator*(const mpreal& a, const mpreal& b) // / Division inline mpreal& mpreal::operator/=(const mpreal& v) { - mpfr_div(mpfr_ptr(),mpfr_srcptr(),v.mpfr_srcptr(),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div(mpfr_ptr(),mpfr_srcptr(),v.mpfr_srcptr(),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const mpz_t v) { - mpfr_div_z(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_z(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const mpq_t v) { - mpfr_div_q(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_q(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const long double v) { - *this /= mpreal(v); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + *this /= mpreal(v); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const double v) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - mpfr_div_d(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + mpfr_div_d(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); #else - *this /= mpreal(v); + *this /= mpreal(v); #endif - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const unsigned long int v) { - mpfr_div_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const unsigned int v) { - mpfr_div_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_ui(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const long int v) { - mpfr_div_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator/=(const int v) { - mpfr_div_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_si(mpfr_ptr(),mpfr_srcptr(),v,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline const mpreal operator/(const mpreal& a, const mpreal& b) @@ -1496,42 +1494,42 @@ inline const mpreal operator/(const mpreal& a, const mpreal& b) inline const mpreal operator/(const unsigned long int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); - mpfr_ui_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); + mpfr_ui_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator/(const unsigned int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); - mpfr_ui_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); + mpfr_ui_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator/(const long int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); - mpfr_si_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); + mpfr_si_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator/(const int b, const mpreal& a) { - mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); - mpfr_si_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); + mpfr_si_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; } inline const mpreal operator/(const double b, const mpreal& a) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); - mpfr_d_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_srcptr())); + mpfr_d_div(x.mpfr_ptr(), b, a.mpfr_srcptr(), mpreal::get_default_rnd()); + return x; #else - mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); - x /= a; - return x; + mpreal x(0, mpfr_get_prec(a.mpfr_ptr())); + x /= a; + return x; #endif } @@ -1539,128 +1537,128 @@ inline const mpreal operator/(const double b, const mpreal& a) // Shifts operators - Multiplication/Division by power of 2 inline mpreal& mpreal::operator<<=(const unsigned long int u) { - mpfr_mul_2ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_2ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator<<=(const unsigned int u) { - mpfr_mul_2ui(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_2ui(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator<<=(const long int u) { - mpfr_mul_2si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_2si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator<<=(const int u) { - mpfr_mul_2si(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_mul_2si(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator>>=(const unsigned long int u) { - mpfr_div_2ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_2ui(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator>>=(const unsigned int u) { - mpfr_div_2ui(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_2ui(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator>>=(const long int u) { - mpfr_div_2si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_2si(mpfr_ptr(),mpfr_srcptr(),u,mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::operator>>=(const int u) { - mpfr_div_2si(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_div_2si(mpfr_ptr(),mpfr_srcptr(),static_cast(u),mpreal::get_default_rnd()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline const mpreal operator<<(const mpreal& v, const unsigned long int k) { - return mul_2ui(v,k); + return mul_2ui(v,k); } inline const mpreal operator<<(const mpreal& v, const unsigned int k) { - return mul_2ui(v,static_cast(k)); + return mul_2ui(v,static_cast(k)); } inline const mpreal operator<<(const mpreal& v, const long int k) { - return mul_2si(v,k); + return mul_2si(v,k); } inline const mpreal operator<<(const mpreal& v, const int k) { - return mul_2si(v,static_cast(k)); + return mul_2si(v,static_cast(k)); } inline const mpreal operator>>(const mpreal& v, const unsigned long int k) { - return div_2ui(v,k); + return div_2ui(v,k); } inline const mpreal operator>>(const mpreal& v, const long int k) { - return div_2si(v,k); + return div_2si(v,k); } inline const mpreal operator>>(const mpreal& v, const unsigned int k) { - return div_2ui(v,static_cast(k)); + return div_2ui(v,static_cast(k)); } inline const mpreal operator>>(const mpreal& v, const int k) { - return div_2si(v,static_cast(k)); + return div_2si(v,static_cast(k)); } // mul_2ui inline const mpreal mul_2ui(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode) { - mpreal x(v); - mpfr_mul_2ui(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); - return x; + mpreal x(v); + mpfr_mul_2ui(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); + return x; } // mul_2si inline const mpreal mul_2si(const mpreal& v, long int k, mp_rnd_t rnd_mode) { - mpreal x(v); - mpfr_mul_2si(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); - return x; + mpreal x(v); + mpfr_mul_2si(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); + return x; } inline const mpreal div_2ui(const mpreal& v, unsigned long int k, mp_rnd_t rnd_mode) { - mpreal x(v); - mpfr_div_2ui(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); - return x; + mpreal x(v); + mpfr_div_2ui(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); + return x; } inline const mpreal div_2si(const mpreal& v, long int k, mp_rnd_t rnd_mode) { - mpreal x(v); - mpfr_div_2si(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); - return x; + mpreal x(v); + mpfr_div_2si(x.mpfr_ptr(),v.mpfr_srcptr(),k,rnd_mode); + return x; } ////////////////////////////////////////////////////////////////////////// @@ -1752,145 +1750,145 @@ inline ::mpfr_srcptr mpreal::mpfr_srcptr() const { return mp; } template inline std::string toString(T t, std::ios_base & (*f)(std::ios_base&)) { - std::ostringstream oss; - oss << f << t; - return oss.str(); + std::ostringstream oss; + oss << f << t; + return oss.str(); } #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) inline std::string mpreal::toString(const std::string& format) const { - char *s = NULL; - std::string out; + char *s = NULL; + std::string out; - if( !format.empty() ) - { - if(!(mpfr_asprintf(&s, format.c_str(), mpfr_srcptr()) < 0)) - { - out = std::string(s); + if( !format.empty() ) + { + if(!(mpfr_asprintf(&s, format.c_str(), mpfr_srcptr()) < 0)) + { + out = std::string(s); - mpfr_free_str(s); - } - } + mpfr_free_str(s); + } + } - return out; + return out; } #endif inline std::string mpreal::toString(int n, int b, mp_rnd_t mode) const { - // TODO: Add extended format specification (f, e, rounding mode) as it done in output operator - (void)b; - (void)mode; + // TODO: Add extended format specification (f, e, rounding mode) as it done in output operator + (void)b; + (void)mode; #if (MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)) - std::ostringstream format; + std::ostringstream format; - int digits = (n >= 0) ? n : 1 + bits2digits(mpfr_get_prec(mpfr_srcptr())); + int digits = (n >= 0) ? n : 1 + bits2digits(mpfr_get_prec(mpfr_srcptr())); - format << "%." << digits << "RNg"; + format << "%." << digits << "RNg"; - return toString(format.str()); + return toString(format.str()); #else - char *s, *ns = NULL; - size_t slen, nslen; - mp_exp_t exp; - std::string out; - - if(mpfr_inf_p(mp)) - { - if(mpfr_sgn(mp)>0) return "+Inf"; - else return "-Inf"; - } - - if(mpfr_zero_p(mp)) return "0"; - if(mpfr_nan_p(mp)) return "NaN"; - - s = mpfr_get_str(NULL, &exp, b, 0, mp, mode); - ns = mpfr_get_str(NULL, &exp, b, (std::max)(0,n), mp, mode); - - if(s!=NULL && ns!=NULL) - { - slen = strlen(s); - nslen = strlen(ns); - if(nslen<=slen) - { - mpfr_free_str(s); - s = ns; - slen = nslen; - } - else { - mpfr_free_str(ns); - } - - // Make human eye-friendly formatting if possible - if (exp>0 && static_cast(exp)s+exp) ptr--; - - if(ptr==s+exp) out = std::string(s,exp+1); - else out = std::string(s,exp+1)+'.'+std::string(s+exp+1,ptr-(s+exp+1)+1); - - //out = string(s,exp+1)+'.'+string(s+exp+1); - } - else - { - // Remove zeros starting from right end - char* ptr = s+slen-1; - while (*ptr=='0' && ptr>s+exp-1) ptr--; - - if(ptr==s+exp-1) out = std::string(s,exp); - else out = std::string(s,exp)+'.'+std::string(s+exp,ptr-(s+exp)+1); - - //out = string(s,exp)+'.'+string(s+exp); - } - - }else{ // exp<0 || exp>slen - if(s[0]=='-') - { - // Remove zeros starting from right end - char* ptr = s+slen-1; - while (*ptr=='0' && ptr>s+1) ptr--; - - if(ptr==s+1) out = std::string(s,2); - else out = std::string(s,2)+'.'+std::string(s+2,ptr-(s+2)+1); - - //out = string(s,2)+'.'+string(s+2); - } - else - { - // Remove zeros starting from right end - char* ptr = s+slen-1; - while (*ptr=='0' && ptr>s) ptr--; - - if(ptr==s) out = std::string(s,1); - else out = std::string(s,1)+'.'+std::string(s+1,ptr-(s+1)+1); - - //out = string(s,1)+'.'+string(s+1); - } - - // Make final string - if(--exp) - { - if(exp>0) out += "e+"+mpfr::toString(exp,std::dec); - else out += "e"+mpfr::toString(exp,std::dec); - } - } - - mpfr_free_str(s); - return out; - }else{ - return "conversion error!"; - } + char *s, *ns = NULL; + size_t slen, nslen; + mp_exp_t exp; + std::string out; + + if(mpfr_inf_p(mp)) + { + if(mpfr_sgn(mp)>0) return "+Inf"; + else return "-Inf"; + } + + if(mpfr_zero_p(mp)) return "0"; + if(mpfr_nan_p(mp)) return "NaN"; + + s = mpfr_get_str(NULL, &exp, b, 0, mp, mode); + ns = mpfr_get_str(NULL, &exp, b, (std::max)(0,n), mp, mode); + + if(s!=NULL && ns!=NULL) + { + slen = strlen(s); + nslen = strlen(ns); + if(nslen<=slen) + { + mpfr_free_str(s); + s = ns; + slen = nslen; + } + else { + mpfr_free_str(ns); + } + + // Make human eye-friendly formatting if possible + if (exp>0 && static_cast(exp)s+exp) ptr--; + + if(ptr==s+exp) out = std::string(s,exp+1); + else out = std::string(s,exp+1)+'.'+std::string(s+exp+1,ptr-(s+exp+1)+1); + + //out = string(s,exp+1)+'.'+string(s+exp+1); + } + else + { + // Remove zeros starting from right end + char* ptr = s+slen-1; + while (*ptr=='0' && ptr>s+exp-1) ptr--; + + if(ptr==s+exp-1) out = std::string(s,exp); + else out = std::string(s,exp)+'.'+std::string(s+exp,ptr-(s+exp)+1); + + //out = string(s,exp)+'.'+string(s+exp); + } + + }else{ // exp<0 || exp>slen + if(s[0]=='-') + { + // Remove zeros starting from right end + char* ptr = s+slen-1; + while (*ptr=='0' && ptr>s+1) ptr--; + + if(ptr==s+1) out = std::string(s,2); + else out = std::string(s,2)+'.'+std::string(s+2,ptr-(s+2)+1); + + //out = string(s,2)+'.'+string(s+2); + } + else + { + // Remove zeros starting from right end + char* ptr = s+slen-1; + while (*ptr=='0' && ptr>s) ptr--; + + if(ptr==s) out = std::string(s,1); + else out = std::string(s,1)+'.'+std::string(s+1,ptr-(s+1)+1); + + //out = string(s,1)+'.'+string(s+1); + } + + // Make final string + if(--exp) + { + if(exp>0) out += "e+"+mpfr::toString(exp,std::dec); + else out += "e"+mpfr::toString(exp,std::dec); + } + } + + mpfr_free_str(s); + return out; + }else{ + return "conversion error!"; + } #endif } @@ -1899,42 +1897,42 @@ inline std::string mpreal::toString(int n, int b, mp_rnd_t mode) const // I/O inline std::ostream& mpreal::output(std::ostream& os) const { - std::ostringstream format; - const std::ios::fmtflags flags = os.flags(); - - format << ((flags & std::ios::showpos) ? "%+" : "%"); - if (os.precision() >= 0) - format << '.' << os.precision() << "R*" - << ((flags & std::ios::floatfield) == std::ios::fixed ? 'f' : - (flags & std::ios::floatfield) == std::ios::scientific ? 'e' : - 'g'); - else - format << "R*e"; - - char *s = NULL; - if(!(mpfr_asprintf(&s, format.str().c_str(), - mpfr::mpreal::get_default_rnd(), - mpfr_srcptr()) - < 0)) - { - os << std::string(s); - mpfr_free_str(s); - } - return os; + std::ostringstream format; + const std::ios::fmtflags flags = os.flags(); + + format << ((flags & std::ios::showpos) ? "%+" : "%"); + if (os.precision() >= 0) + format << '.' << os.precision() << "R*" + << ((flags & std::ios::floatfield) == std::ios::fixed ? 'f' : + (flags & std::ios::floatfield) == std::ios::scientific ? 'e' : + 'g'); + else + format << "R*e"; + + char *s = NULL; + if(!(mpfr_asprintf(&s, format.str().c_str(), + mpfr::mpreal::get_default_rnd(), + mpfr_srcptr()) + < 0)) + { + os << std::string(s); + mpfr_free_str(s); + } + return os; } inline std::ostream& operator<<(std::ostream& os, const mpreal& v) { - return v.output(os); + return v.output(os); } inline std::istream& operator>>(std::istream &is, mpreal& v) { - // TODO: use cout::hexfloat and other flags to setup base - std::string tmp; - is >> tmp; - mpfr_set_str(v.mpfr_ptr(), tmp.c_str(), 10, mpreal::get_default_rnd()); - return is; + // TODO: use cout::hexfloat and other flags to setup base + std::string tmp; + is >> tmp; + mpfr_set_str(v.mpfr_ptr(), tmp.c_str(), 10, mpreal::get_default_rnd()); + return is; } ////////////////////////////////////////////////////////////////////////// @@ -1944,245 +1942,245 @@ inline std::istream& operator>>(std::istream &is, mpreal& v) inline mp_prec_t digits2bits(int d) { - const double LOG2_10 = 3.3219280948873624; + const double LOG2_10 = 3.3219280948873624; - return mp_prec_t(std::ceil( d * LOG2_10 )); + return mp_prec_t(std::ceil( d * LOG2_10 )); } inline int bits2digits(mp_prec_t b) { - const double LOG10_2 = 0.30102999566398119; + const double LOG10_2 = 0.30102999566398119; - return int(std::floor( b * LOG10_2 )); + return int(std::floor( b * LOG10_2 )); } ////////////////////////////////////////////////////////////////////////// // Set/Get number properties inline int sgn(const mpreal& op) { - return mpfr_sgn(op.mpfr_srcptr()); + return mpfr_sgn(op.mpfr_srcptr()); } inline mpreal& mpreal::setSign(int sign, mp_rnd_t RoundingMode) { - mpfr_setsign(mpfr_ptr(), mpfr_srcptr(), (sign < 0 ? 1 : 0), RoundingMode); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_setsign(mpfr_ptr(), mpfr_srcptr(), (sign < 0 ? 1 : 0), RoundingMode); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline int mpreal::getPrecision() const { - return int(mpfr_get_prec(mpfr_srcptr())); + return int(mpfr_get_prec(mpfr_srcptr())); } inline mpreal& mpreal::setPrecision(int Precision, mp_rnd_t RoundingMode) { - mpfr_prec_round(mpfr_ptr(), Precision, RoundingMode); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_prec_round(mpfr_ptr(), Precision, RoundingMode); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::setInf(int sign) { - mpfr_set_inf(mpfr_ptr(), sign); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_set_inf(mpfr_ptr(), sign); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::setNan() { - mpfr_set_nan(mpfr_ptr()); - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + mpfr_set_nan(mpfr_ptr()); + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mpreal& mpreal::setZero(int sign) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(3,0,0)) - mpfr_set_zero(mpfr_ptr(), sign); + mpfr_set_zero(mpfr_ptr(), sign); #else - mpfr_set_si(mpfr_ptr(), 0, (mpfr_get_default_rounding_mode)()); - setSign(sign); + mpfr_set_si(mpfr_ptr(), 0, (mpfr_get_default_rounding_mode)()); + setSign(sign); #endif - MPREAL_MSVC_DEBUGVIEW_CODE; - return *this; + MPREAL_MSVC_DEBUGVIEW_CODE; + return *this; } inline mp_prec_t mpreal::get_prec() const { - return mpfr_get_prec(mpfr_srcptr()); + return mpfr_get_prec(mpfr_srcptr()); } inline void mpreal::set_prec(mp_prec_t prec, mp_rnd_t rnd_mode) { - mpfr_prec_round(mpfr_ptr(),prec,rnd_mode); - MPREAL_MSVC_DEBUGVIEW_CODE; + mpfr_prec_round(mpfr_ptr(),prec,rnd_mode); + MPREAL_MSVC_DEBUGVIEW_CODE; } inline mp_exp_t mpreal::get_exp () { - return mpfr_get_exp(mpfr_srcptr()); + return mpfr_get_exp(mpfr_srcptr()); } inline int mpreal::set_exp (mp_exp_t e) { - int x = mpfr_set_exp(mpfr_ptr(), e); - MPREAL_MSVC_DEBUGVIEW_CODE; - return x; + int x = mpfr_set_exp(mpfr_ptr(), e); + MPREAL_MSVC_DEBUGVIEW_CODE; + return x; } inline const mpreal frexp(const mpreal& v, mp_exp_t* exp) { - mpreal x(v); - *exp = x.get_exp(); - x.set_exp(0); - return x; + mpreal x(v); + *exp = x.get_exp(); + x.set_exp(0); + return x; } inline const mpreal ldexp(const mpreal& v, mp_exp_t exp) { - mpreal x(v); + mpreal x(v); - // rounding is not important since we are just increasing the exponent (= exact operation) - mpfr_mul_2si(x.mpfr_ptr(), x.mpfr_srcptr(), exp, mpreal::get_default_rnd()); - return x; + // rounding is not important since we are just increasing the exponent (= exact operation) + mpfr_mul_2si(x.mpfr_ptr(), x.mpfr_srcptr(), exp, mpreal::get_default_rnd()); + return x; } inline const mpreal scalbn(const mpreal& v, mp_exp_t exp) { - return ldexp(v, exp); + return ldexp(v, exp); } inline mpreal machine_epsilon(mp_prec_t prec) { - /* the smallest eps such that 1 + eps != 1 */ - return machine_epsilon(mpreal(1, prec)); + /* the smallest eps such that 1 + eps != 1 */ + return machine_epsilon(mpreal(1, prec)); } inline mpreal machine_epsilon(const mpreal& x) { - /* the smallest eps such that x + eps != x */ - if( x < 0) - { - return nextabove(-x) + x; - }else{ - return nextabove( x) - x; - } + /* the smallest eps such that x + eps != x */ + if( x < 0) + { + return nextabove(-x) + x; + }else{ + return nextabove( x) - x; + } } // minval is 'safe' meaning 1 / minval does not overflow inline mpreal minval(mp_prec_t prec) { - /* min = 1/2 * 2^emin = 2^(emin - 1) */ - return mpreal(1, prec) << mpreal::get_emin()-1; + /* min = 1/2 * 2^emin = 2^(emin - 1) */ + return mpreal(1, prec) << mpreal::get_emin()-1; } // maxval is 'safe' meaning 1 / maxval does not underflow inline mpreal maxval(mp_prec_t prec) { - /* max = (1 - eps) * 2^emax, eps is machine epsilon */ - return (mpreal(1, prec) - machine_epsilon(prec)) << mpreal::get_emax(); + /* max = (1 - eps) * 2^emax, eps is machine epsilon */ + return (mpreal(1, prec) - machine_epsilon(prec)) << mpreal::get_emax(); } inline bool isEqualUlps(const mpreal& a, const mpreal& b, int maxUlps) { - return abs(a - b) <= machine_epsilon((max)(abs(a), abs(b))) * maxUlps; + return abs(a - b) <= machine_epsilon((max)(abs(a), abs(b))) * maxUlps; } inline bool isEqualFuzzy(const mpreal& a, const mpreal& b, const mpreal& eps) { - return abs(a - b) <= eps; + return abs(a - b) <= eps; } inline bool isEqualFuzzy(const mpreal& a, const mpreal& b) { - return isEqualFuzzy(a, b, machine_epsilon((max)(1, (min)(abs(a), abs(b))))); + return isEqualFuzzy(a, b, machine_epsilon((max)(1, (min)(abs(a), abs(b))))); } ////////////////////////////////////////////////////////////////////////// // C++11 sign functions. inline mpreal copysign(const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal rop(0, mpfr_get_prec(x.mpfr_ptr())); - mpfr_setsign(rop.mpfr_ptr(), x.mpfr_srcptr(), mpfr_signbit(y.mpfr_srcptr()), rnd_mode); - return rop; + mpreal rop(0, mpfr_get_prec(x.mpfr_ptr())); + mpfr_setsign(rop.mpfr_ptr(), x.mpfr_srcptr(), mpfr_signbit(y.mpfr_srcptr()), rnd_mode); + return rop; } inline bool signbit(const mpreal& x) { - return mpfr_signbit(x.mpfr_srcptr()); + return mpfr_signbit(x.mpfr_srcptr()); } inline const mpreal modf(const mpreal& v, mpreal& n) { - mpreal f(v); + mpreal f(v); - // rounding is not important since we are using the same number - mpfr_frac (f.mpfr_ptr(),f.mpfr_srcptr(),mpreal::get_default_rnd()); - mpfr_trunc(n.mpfr_ptr(),v.mpfr_srcptr()); - return f; + // rounding is not important since we are using the same number + mpfr_frac (f.mpfr_ptr(),f.mpfr_srcptr(),mpreal::get_default_rnd()); + mpfr_trunc(n.mpfr_ptr(),v.mpfr_srcptr()); + return f; } inline int mpreal::check_range (int t, mp_rnd_t rnd_mode) { - return mpfr_check_range(mpfr_ptr(),t,rnd_mode); + return mpfr_check_range(mpfr_ptr(),t,rnd_mode); } inline int mpreal::subnormalize (int t,mp_rnd_t rnd_mode) { - int r = mpfr_subnormalize(mpfr_ptr(),t,rnd_mode); - MPREAL_MSVC_DEBUGVIEW_CODE; - return r; + int r = mpfr_subnormalize(mpfr_ptr(),t,rnd_mode); + MPREAL_MSVC_DEBUGVIEW_CODE; + return r; } inline mp_exp_t mpreal::get_emin (void) { - return mpfr_get_emin(); + return mpfr_get_emin(); } inline int mpreal::set_emin (mp_exp_t exp) { - return mpfr_set_emin(exp); + return mpfr_set_emin(exp); } inline mp_exp_t mpreal::get_emax (void) { - return mpfr_get_emax(); + return mpfr_get_emax(); } inline int mpreal::set_emax (mp_exp_t exp) { - return mpfr_set_emax(exp); + return mpfr_set_emax(exp); } inline mp_exp_t mpreal::get_emin_min (void) { - return mpfr_get_emin_min(); + return mpfr_get_emin_min(); } inline mp_exp_t mpreal::get_emin_max (void) { - return mpfr_get_emin_max(); + return mpfr_get_emin_max(); } inline mp_exp_t mpreal::get_emax_min (void) { - return mpfr_get_emax_min(); + return mpfr_get_emax_min(); } inline mp_exp_t mpreal::get_emax_max (void) { - return mpfr_get_emax_max(); + return mpfr_get_emax_max(); } ////////////////////////////////////////////////////////////////////////// // Mathematical Functions ////////////////////////////////////////////////////////////////////////// #define MPREAL_UNARY_MATH_FUNCTION_BODY(f) \ - mpreal y(0, mpfr_get_prec(x.mpfr_srcptr())); \ - mpfr_##f(y.mpfr_ptr(), x.mpfr_srcptr(), r); \ - return y; + mpreal y(0, mpfr_get_prec(x.mpfr_srcptr())); \ + mpfr_##f(y.mpfr_ptr(), x.mpfr_srcptr(), r); \ + return y; inline const mpreal sqr (const mpreal& x, mp_rnd_t r = mpreal::get_default_rnd()) { MPREAL_UNARY_MATH_FUNCTION_BODY(sqr ); } @@ -2192,50 +2190,50 @@ inline const mpreal sqrt (const mpreal& x, mp_rnd_t r = mpreal::get_default_rnd( inline const mpreal sqrt(const unsigned long int x, mp_rnd_t r) { - mpreal y; - mpfr_sqrt_ui(y.mpfr_ptr(), x, r); - return y; + mpreal y; + mpfr_sqrt_ui(y.mpfr_ptr(), x, r); + return y; } inline const mpreal sqrt(const unsigned int v, mp_rnd_t rnd_mode) { - return sqrt(static_cast(v),rnd_mode); + return sqrt(static_cast(v),rnd_mode); } inline const mpreal sqrt(const long int v, mp_rnd_t rnd_mode) { - if (v>=0) return sqrt(static_cast(v),rnd_mode); - else return mpreal().setNan(); // NaN + if (v>=0) return sqrt(static_cast(v),rnd_mode); + else return mpreal().setNan(); // NaN } inline const mpreal sqrt(const int v, mp_rnd_t rnd_mode) { - if (v>=0) return sqrt(static_cast(v),rnd_mode); - else return mpreal().setNan(); // NaN + if (v>=0) return sqrt(static_cast(v),rnd_mode); + else return mpreal().setNan(); // NaN } inline const mpreal root(const mpreal& x, unsigned long int k, mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal y(0, mpfr_get_prec(x.mpfr_srcptr())); - mpfr_root(y.mpfr_ptr(), x.mpfr_srcptr(), k, r); - return y; + mpreal y(0, mpfr_get_prec(x.mpfr_srcptr())); + mpfr_root(y.mpfr_ptr(), x.mpfr_srcptr(), k, r); + return y; } inline const mpreal dim(const mpreal& a, const mpreal& b, mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal y(0, mpfr_get_prec(a.mpfr_srcptr())); - mpfr_dim(y.mpfr_ptr(), a.mpfr_srcptr(), b.mpfr_srcptr(), r); - return y; + mpreal y(0, mpfr_get_prec(a.mpfr_srcptr())); + mpfr_dim(y.mpfr_ptr(), a.mpfr_srcptr(), b.mpfr_srcptr(), r); + return y; } inline int cmpabs(const mpreal& a,const mpreal& b) { - return mpfr_cmpabs(a.mpfr_ptr(), b.mpfr_srcptr()); + return mpfr_cmpabs(a.mpfr_ptr(), b.mpfr_srcptr()); } inline int sin_cos(mpreal& s, mpreal& c, const mpreal& v, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - return mpfr_sin_cos(s.mpfr_ptr(), c.mpfr_ptr(), v.mpfr_srcptr(), rnd_mode); + return mpfr_sin_cos(s.mpfr_ptr(), c.mpfr_ptr(), v.mpfr_srcptr(), rnd_mode); } inline const mpreal sqrt (const long double v, mp_rnd_t rnd_mode) { return sqrt(mpreal(v),rnd_mode); } @@ -2295,124 +2293,124 @@ inline const mpreal bessely1(const mpreal& x, mp_rnd_t r = mpreal::get_default_r inline const mpreal atan2 (const mpreal& y, const mpreal& x, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); - mpfr_atan2(a.mpfr_ptr(), y.mpfr_srcptr(), x.mpfr_srcptr(), rnd_mode); - return a; + mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); + mpfr_atan2(a.mpfr_ptr(), y.mpfr_srcptr(), x.mpfr_srcptr(), rnd_mode); + return a; } inline const mpreal hypot (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); - mpfr_hypot(a.mpfr_ptr(), x.mpfr_srcptr(), y.mpfr_srcptr(), rnd_mode); - return a; + mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); + mpfr_hypot(a.mpfr_ptr(), x.mpfr_srcptr(), y.mpfr_srcptr(), rnd_mode); + return a; } inline const mpreal remainder (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); - mpfr_remainder(a.mpfr_ptr(), x.mpfr_srcptr(), y.mpfr_srcptr(), rnd_mode); - return a; + mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); + mpfr_remainder(a.mpfr_ptr(), x.mpfr_srcptr(), y.mpfr_srcptr(), rnd_mode); + return a; } inline const mpreal remquo (long* q, const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); - mpfr_remquo(a.mpfr_ptr(),q, x.mpfr_srcptr(), y.mpfr_srcptr(), rnd_mode); - return a; + mpreal a(0,(std::max)(y.getPrecision(), x.getPrecision())); + mpfr_remquo(a.mpfr_ptr(),q, x.mpfr_srcptr(), y.mpfr_srcptr(), rnd_mode); + return a; } inline const mpreal fac_ui (unsigned long int v, mp_prec_t prec = mpreal::get_default_prec(), - mp_rnd_t rnd_mode = mpreal::get_default_rnd()) + mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(0, prec); - mpfr_fac_ui(x.mpfr_ptr(),v,rnd_mode); - return x; + mpreal x(0, prec); + mpfr_fac_ui(x.mpfr_ptr(),v,rnd_mode); + return x; } inline const mpreal lgamma (const mpreal& v, int *signp = 0, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(v); - int tsignp; + mpreal x(v); + int tsignp; - if(signp) mpfr_lgamma(x.mpfr_ptr(), signp,v.mpfr_srcptr(),rnd_mode); - else mpfr_lgamma(x.mpfr_ptr(),&tsignp,v.mpfr_srcptr(),rnd_mode); + if(signp) mpfr_lgamma(x.mpfr_ptr(), signp,v.mpfr_srcptr(),rnd_mode); + else mpfr_lgamma(x.mpfr_ptr(),&tsignp,v.mpfr_srcptr(),rnd_mode); - return x; + return x; } inline const mpreal besseljn (long n, const mpreal& x, mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal y(0, x.getPrecision()); - mpfr_jn(y.mpfr_ptr(), n, x.mpfr_srcptr(), r); - return y; + mpreal y(0, x.getPrecision()); + mpfr_jn(y.mpfr_ptr(), n, x.mpfr_srcptr(), r); + return y; } inline const mpreal besselyn (long n, const mpreal& x, mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal y(0, x.getPrecision()); - mpfr_yn(y.mpfr_ptr(), n, x.mpfr_srcptr(), r); - return y; + mpreal y(0, x.getPrecision()); + mpfr_yn(y.mpfr_ptr(), n, x.mpfr_srcptr(), r); + return y; } inline const mpreal fma (const mpreal& v1, const mpreal& v2, const mpreal& v3, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a; - mp_prec_t p1, p2, p3; + mpreal a; + mp_prec_t p1, p2, p3; - p1 = v1.get_prec(); - p2 = v2.get_prec(); - p3 = v3.get_prec(); + p1 = v1.get_prec(); + p2 = v2.get_prec(); + p3 = v3.get_prec(); - a.set_prec(p3>p2?(p3>p1?p3:p1):(p2>p1?p2:p1)); + a.set_prec(p3>p2?(p3>p1?p3:p1):(p2>p1?p2:p1)); - mpfr_fma(a.mp,v1.mp,v2.mp,v3.mp,rnd_mode); - return a; + mpfr_fma(a.mp,v1.mp,v2.mp,v3.mp,rnd_mode); + return a; } inline const mpreal fms (const mpreal& v1, const mpreal& v2, const mpreal& v3, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a; - mp_prec_t p1, p2, p3; + mpreal a; + mp_prec_t p1, p2, p3; - p1 = v1.get_prec(); - p2 = v2.get_prec(); - p3 = v3.get_prec(); + p1 = v1.get_prec(); + p2 = v2.get_prec(); + p3 = v3.get_prec(); - a.set_prec(p3>p2?(p3>p1?p3:p1):(p2>p1?p2:p1)); + a.set_prec(p3>p2?(p3>p1?p3:p1):(p2>p1?p2:p1)); - mpfr_fms(a.mp,v1.mp,v2.mp,v3.mp,rnd_mode); - return a; + mpfr_fms(a.mp,v1.mp,v2.mp,v3.mp,rnd_mode); + return a; } inline const mpreal agm (const mpreal& v1, const mpreal& v2, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a; - mp_prec_t p1, p2; + mpreal a; + mp_prec_t p1, p2; - p1 = v1.get_prec(); - p2 = v2.get_prec(); + p1 = v1.get_prec(); + p2 = v2.get_prec(); - a.set_prec(p1>p2?p1:p2); + a.set_prec(p1>p2?p1:p2); - mpfr_agm(a.mp, v1.mp, v2.mp, rnd_mode); + mpfr_agm(a.mp, v1.mp, v2.mp, rnd_mode); - return a; + return a; } inline const mpreal sum (const mpreal tab[], const unsigned long int n, int& status, mp_rnd_t mode = mpreal::get_default_rnd()) { - mpfr_srcptr *p = new mpfr_srcptr[n]; + mpfr_srcptr *p = new mpfr_srcptr[n]; - for (unsigned long int i = 0; i < n; i++) - p[i] = tab[i].mpfr_srcptr(); + for (unsigned long int i = 0; i < n; i++) + p[i] = tab[i].mpfr_srcptr(); - mpreal x; - status = mpfr_sum(x.mpfr_ptr(), (mpfr_ptr*)p, n, mode); + mpreal x; + status = mpfr_sum(x.mpfr_ptr(), (mpfr_ptr*)p, n, mode); - delete [] p; - return x; + delete [] p; + return x; } ////////////////////////////////////////////////////////////////////////// @@ -2421,65 +2419,65 @@ inline const mpreal sum (const mpreal tab[], const unsigned long int n, int& sta inline int sinh_cosh(mpreal& s, mpreal& c, const mpreal& v, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - return mpfr_sinh_cosh(s.mp,c.mp,v.mp,rnd_mode); + return mpfr_sinh_cosh(s.mp,c.mp,v.mp,rnd_mode); } inline const mpreal li2 (const mpreal& x, mp_rnd_t r = mpreal::get_default_rnd()) { - MPREAL_UNARY_MATH_FUNCTION_BODY(li2); + MPREAL_UNARY_MATH_FUNCTION_BODY(li2); } inline const mpreal rem (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - /* R = rem(X,Y) if Y != 0, returns X - n * Y where n = trunc(X/Y). */ - return fmod(x, y, rnd_mode); + /* R = rem(X,Y) if Y != 0, returns X - n * Y where n = trunc(X/Y). */ + return fmod(x, y, rnd_mode); } inline const mpreal mod (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - (void)rnd_mode; + (void)rnd_mode; - /* + /* - m = mod(x,y) if y != 0, returns x - n*y where n = floor(x/y) + m = mod(x,y) if y != 0, returns x - n*y where n = floor(x/y) - The following are true by convention: - - mod(x,0) is x - - mod(x,x) is 0 - - mod(x,y) for x != y and y != 0 has the same sign as y. + The following are true by convention: + - mod(x,0) is x + - mod(x,x) is 0 + - mod(x,y) for x != y and y != 0 has the same sign as y. - */ + */ - if(iszero(y)) return x; - if(x == y) return 0; + if(iszero(y)) return x; + if(x == y) return 0; - mpreal m = x - floor(x / y) * y; + mpreal m = x - floor(x / y) * y; - m.setSign(sgn(y)); // make sure result has the same sign as Y + m.setSign(sgn(y)); // make sure result has the same sign as Y - return m; + return m; } inline const mpreal fmod (const mpreal& x, const mpreal& y, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal a; - mp_prec_t yp, xp; + mpreal a; + mp_prec_t yp, xp; - yp = y.get_prec(); - xp = x.get_prec(); + yp = y.get_prec(); + xp = x.get_prec(); - a.set_prec(yp>xp?yp:xp); + a.set_prec(yp>xp?yp:xp); - mpfr_fmod(a.mp, x.mp, y.mp, rnd_mode); + mpfr_fmod(a.mp, x.mp, y.mp, rnd_mode); - return a; + return a; } inline const mpreal rec_sqrt(const mpreal& v, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(v); - mpfr_rec_sqrt(x.mp,v.mp,rnd_mode); - return x; + mpreal x(v); + mpfr_rec_sqrt(x.mp,v.mp,rnd_mode); + return x; } #endif // MPFR 2.4.0 Specifics @@ -2494,67 +2492,67 @@ inline const mpreal ai (const mpreal& x, mp_rnd_t r = mpreal::get_default_r // Constants inline const mpreal const_log2 (mp_prec_t p = mpreal::get_default_prec(), mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal x(0, p); - mpfr_const_log2(x.mpfr_ptr(), r); - return x; + mpreal x(0, p); + mpfr_const_log2(x.mpfr_ptr(), r); + return x; } inline const mpreal const_pi (mp_prec_t p = mpreal::get_default_prec(), mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal x(0, p); - mpfr_const_pi(x.mpfr_ptr(), r); - return x; + mpreal x(0, p); + mpfr_const_pi(x.mpfr_ptr(), r); + return x; } inline const mpreal const_euler (mp_prec_t p = mpreal::get_default_prec(), mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal x(0, p); - mpfr_const_euler(x.mpfr_ptr(), r); - return x; + mpreal x(0, p); + mpfr_const_euler(x.mpfr_ptr(), r); + return x; } inline const mpreal const_catalan (mp_prec_t p = mpreal::get_default_prec(), mp_rnd_t r = mpreal::get_default_rnd()) { - mpreal x(0, p); - mpfr_const_catalan(x.mpfr_ptr(), r); - return x; + mpreal x(0, p); + mpfr_const_catalan(x.mpfr_ptr(), r); + return x; } inline const mpreal const_infinity (int sign = 1, mp_prec_t p = mpreal::get_default_prec()) { - mpreal x(0, p); - mpfr_set_inf(x.mpfr_ptr(), sign); - return x; + mpreal x(0, p); + mpfr_set_inf(x.mpfr_ptr(), sign); + return x; } ////////////////////////////////////////////////////////////////////////// // Integer Related Functions inline const mpreal ceil(const mpreal& v) { - mpreal x(v); - mpfr_ceil(x.mp,v.mp); - return x; + mpreal x(v); + mpfr_ceil(x.mp,v.mp); + return x; } inline const mpreal floor(const mpreal& v) { - mpreal x(v); - mpfr_floor(x.mp,v.mp); - return x; + mpreal x(v); + mpfr_floor(x.mp,v.mp); + return x; } inline const mpreal round(const mpreal& v) { - mpreal x(v); - mpfr_round(x.mp,v.mp); - return x; + mpreal x(v); + mpfr_round(x.mp,v.mp); + return x; } inline const mpreal trunc(const mpreal& v) { - mpreal x(v); - mpfr_trunc(x.mp,v.mp); - return x; + mpreal x(v); + mpfr_trunc(x.mp,v.mp); + return x; } inline const mpreal rint (const mpreal& x, mp_rnd_t r = mpreal::get_default_rnd()) { MPREAL_UNARY_MATH_FUNCTION_BODY(rint ); } @@ -2572,61 +2570,61 @@ inline const mpreal (min)(const mpreal& x, const mpreal& y){ return (x= MPFR_VERSION_NUM(3,0,0)) inline const mpreal urandom (gmp_randstate_t& state, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x; - mpfr_urandom(x.mpfr_ptr(), state, rnd_mode); - return x; + mpreal x; + mpfr_urandom(x.mpfr_ptr(), state, rnd_mode); + return x; } #endif #if (MPFR_VERSION <= MPFR_VERSION_NUM(2,4,2)) inline const mpreal random2 (mp_size_t size, mp_exp_t exp) { - mpreal x; - mpfr_random2(x.mpfr_ptr(),size,exp); - return x; + mpreal x; + mpfr_random2(x.mpfr_ptr(),size,exp); + return x; } #endif @@ -2637,22 +2635,22 @@ inline const mpreal random2 (mp_size_t size, mp_exp_t exp) inline const mpreal random(unsigned int seed = 0) { #if (MPFR_VERSION >= MPFR_VERSION_NUM(3,0,0)) - static gmp_randstate_t state; - static bool initialize = true; + static gmp_randstate_t state; + static bool initialize = true; - if(initialize) - { - gmp_randinit_default(state); - gmp_randseed_ui(state,0); - initialize = false; - } + if(initialize) + { + gmp_randinit_default(state); + gmp_randseed_ui(state,0); + initialize = false; + } - if(seed != 0) gmp_randseed_ui(state,seed); + if(seed != 0) gmp_randseed_ui(state,seed); - return mpfr::urandom(state); + return mpfr::urandom(state); #else - if(seed != 0) std::srand(seed); - return mpfr::mpreal(std::rand()/(double)RAND_MAX); + if(seed != 0) std::srand(seed); + return mpfr::mpreal(std::rand()/(double)RAND_MAX); #endif } @@ -2661,26 +2659,26 @@ inline const mpreal random(unsigned int seed = 0) inline const mpreal grandom (gmp_randstate_t& state, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x; - mpfr_grandom(x.mpfr_ptr(), NULL, state, rnd_mode); - return x; + mpreal x; + mpfr_grandom(x.mpfr_ptr(), NULL, state, rnd_mode); + return x; } inline const mpreal grandom(unsigned int seed = 0) { - static gmp_randstate_t state; - static bool initialize = true; + static gmp_randstate_t state; + static bool initialize = true; - if(initialize) - { - gmp_randinit_default(state); - gmp_randseed_ui(state,0); - initialize = false; - } + if(initialize) + { + gmp_randinit_default(state); + gmp_randseed_ui(state,0); + initialize = false; + } - if(seed != 0) gmp_randseed_ui(state,seed); + if(seed != 0) gmp_randseed_ui(state,seed); - return mpfr::grandom(state); + return mpfr::grandom(state); } #endif @@ -2688,314 +2686,314 @@ inline const mpreal grandom(unsigned int seed = 0) // Set/Get global properties inline void mpreal::set_default_prec(mp_prec_t prec) { - mpfr_set_default_prec(prec); + mpfr_set_default_prec(prec); } inline void mpreal::set_default_rnd(mp_rnd_t rnd_mode) { - mpfr_set_default_rounding_mode(rnd_mode); + mpfr_set_default_rounding_mode(rnd_mode); } inline bool mpreal::fits_in_bits(double x, int n) { - int i; - double t; - return IsInf(x) || (std::modf ( std::ldexp ( std::frexp ( x, &i ), n ), &t ) == 0.0); + int i; + double t; + return IsInf(x) || (std::modf ( std::ldexp ( std::frexp ( x, &i ), n ), &t ) == 0.0); } inline const mpreal pow(const mpreal& a, const mpreal& b, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(a); - mpfr_pow(x.mp,x.mp,b.mp,rnd_mode); - return x; + mpreal x(a); + mpfr_pow(x.mp,x.mp,b.mp,rnd_mode); + return x; } inline const mpreal pow(const mpreal& a, const mpz_t b, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(a); - mpfr_pow_z(x.mp,x.mp,b,rnd_mode); - return x; + mpreal x(a); + mpfr_pow_z(x.mp,x.mp,b,rnd_mode); + return x; } inline const mpreal pow(const mpreal& a, const unsigned long int b, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(a); - mpfr_pow_ui(x.mp,x.mp,b,rnd_mode); - return x; + mpreal x(a); + mpfr_pow_ui(x.mp,x.mp,b,rnd_mode); + return x; } inline const mpreal pow(const mpreal& a, const unsigned int b, mp_rnd_t rnd_mode) { - return pow(a,static_cast(b),rnd_mode); + return pow(a,static_cast(b),rnd_mode); } inline const mpreal pow(const mpreal& a, const long int b, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(a); - mpfr_pow_si(x.mp,x.mp,b,rnd_mode); - return x; + mpreal x(a); + mpfr_pow_si(x.mp,x.mp,b,rnd_mode); + return x; } inline const mpreal pow(const mpreal& a, const int b, mp_rnd_t rnd_mode) { - return pow(a,static_cast(b),rnd_mode); + return pow(a,static_cast(b),rnd_mode); } inline const mpreal pow(const mpreal& a, const long double b, mp_rnd_t rnd_mode) { - return pow(a,mpreal(b),rnd_mode); + return pow(a,mpreal(b),rnd_mode); } inline const mpreal pow(const mpreal& a, const double b, mp_rnd_t rnd_mode) { - return pow(a,mpreal(b),rnd_mode); + return pow(a,mpreal(b),rnd_mode); } inline const mpreal pow(const unsigned long int a, const mpreal& b, mp_rnd_t rnd_mode = mpreal::get_default_rnd()) { - mpreal x(a); - mpfr_ui_pow(x.mp,a,b.mp,rnd_mode); - return x; + mpreal x(a); + mpfr_ui_pow(x.mp,a,b.mp,rnd_mode); + return x; } inline const mpreal pow(const unsigned int a, const mpreal& b, mp_rnd_t rnd_mode) { - return pow(static_cast(a),b,rnd_mode); + return pow(static_cast(a),b,rnd_mode); } inline const mpreal pow(const long int a, const mpreal& b, mp_rnd_t rnd_mode) { - if (a>=0) return pow(static_cast(a),b,rnd_mode); - else return pow(mpreal(a),b,rnd_mode); + if (a>=0) return pow(static_cast(a),b,rnd_mode); + else return pow(mpreal(a),b,rnd_mode); } inline const mpreal pow(const int a, const mpreal& b, mp_rnd_t rnd_mode) { - if (a>=0) return pow(static_cast(a),b,rnd_mode); - else return pow(mpreal(a),b,rnd_mode); + if (a>=0) return pow(static_cast(a),b,rnd_mode); + else return pow(mpreal(a),b,rnd_mode); } inline const mpreal pow(const long double a, const mpreal& b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),b,rnd_mode); + return pow(mpreal(a),b,rnd_mode); } inline const mpreal pow(const double a, const mpreal& b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),b,rnd_mode); + return pow(mpreal(a),b,rnd_mode); } // pow unsigned long int inline const mpreal pow(const unsigned long int a, const unsigned long int b, mp_rnd_t rnd_mode) { - mpreal x(a); - mpfr_ui_pow_ui(x.mp,a,b,rnd_mode); - return x; + mpreal x(a); + mpfr_ui_pow_ui(x.mp,a,b,rnd_mode); + return x; } inline const mpreal pow(const unsigned long int a, const unsigned int b, mp_rnd_t rnd_mode) { - return pow(a,static_cast(b),rnd_mode); //mpfr_ui_pow_ui + return pow(a,static_cast(b),rnd_mode); //mpfr_ui_pow_ui } inline const mpreal pow(const unsigned long int a, const long int b, mp_rnd_t rnd_mode) { - if(b>0) return pow(a,static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow + if(b>0) return pow(a,static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow } inline const mpreal pow(const unsigned long int a, const int b, mp_rnd_t rnd_mode) { - if(b>0) return pow(a,static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow + if(b>0) return pow(a,static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow } inline const mpreal pow(const unsigned long int a, const long double b, mp_rnd_t rnd_mode) { - return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow + return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow } inline const mpreal pow(const unsigned long int a, const double b, mp_rnd_t rnd_mode) { - return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow + return pow(a,mpreal(b),rnd_mode); //mpfr_ui_pow } // pow unsigned int inline const mpreal pow(const unsigned int a, const unsigned long int b, mp_rnd_t rnd_mode) { - return pow(static_cast(a),b,rnd_mode); //mpfr_ui_pow_ui + return pow(static_cast(a),b,rnd_mode); //mpfr_ui_pow_ui } inline const mpreal pow(const unsigned int a, const unsigned int b, mp_rnd_t rnd_mode) { - return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui } inline const mpreal pow(const unsigned int a, const long int b, mp_rnd_t rnd_mode) { - if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow } inline const mpreal pow(const unsigned int a, const int b, mp_rnd_t rnd_mode) { - if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow } inline const mpreal pow(const unsigned int a, const long double b, mp_rnd_t rnd_mode) { - return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow } inline const mpreal pow(const unsigned int a, const double b, mp_rnd_t rnd_mode) { - return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow } // pow long int inline const mpreal pow(const long int a, const unsigned long int b, mp_rnd_t rnd_mode) { - if (a>0) return pow(static_cast(a),b,rnd_mode); //mpfr_ui_pow_ui - else return pow(mpreal(a),b,rnd_mode); //mpfr_pow_ui + if (a>0) return pow(static_cast(a),b,rnd_mode); //mpfr_ui_pow_ui + else return pow(mpreal(a),b,rnd_mode); //mpfr_pow_ui } inline const mpreal pow(const long int a, const unsigned int b, mp_rnd_t rnd_mode) { - if (a>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(mpreal(a),static_cast(b),rnd_mode); //mpfr_pow_ui + if (a>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(mpreal(a),static_cast(b),rnd_mode); //mpfr_pow_ui } inline const mpreal pow(const long int a, const long int b, mp_rnd_t rnd_mode) { - if (a>0) - { - if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - }else{ - return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si - } + if (a>0) + { + if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + }else{ + return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si + } } inline const mpreal pow(const long int a, const int b, mp_rnd_t rnd_mode) { - if (a>0) - { - if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - }else{ - return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si - } + if (a>0) + { + if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + }else{ + return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si + } } inline const mpreal pow(const long int a, const long double b, mp_rnd_t rnd_mode) { - if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow + if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow } inline const mpreal pow(const long int a, const double b, mp_rnd_t rnd_mode) { - if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow + if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow } // pow int inline const mpreal pow(const int a, const unsigned long int b, mp_rnd_t rnd_mode) { - if (a>0) return pow(static_cast(a),b,rnd_mode); //mpfr_ui_pow_ui - else return pow(mpreal(a),b,rnd_mode); //mpfr_pow_ui + if (a>0) return pow(static_cast(a),b,rnd_mode); //mpfr_ui_pow_ui + else return pow(mpreal(a),b,rnd_mode); //mpfr_pow_ui } inline const mpreal pow(const int a, const unsigned int b, mp_rnd_t rnd_mode) { - if (a>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(mpreal(a),static_cast(b),rnd_mode); //mpfr_pow_ui + if (a>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(mpreal(a),static_cast(b),rnd_mode); //mpfr_pow_ui } inline const mpreal pow(const int a, const long int b, mp_rnd_t rnd_mode) { - if (a>0) - { - if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - }else{ - return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si - } + if (a>0) + { + if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + }else{ + return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si + } } inline const mpreal pow(const int a, const int b, mp_rnd_t rnd_mode) { - if (a>0) - { - if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui - else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - }else{ - return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si - } + if (a>0) + { + if(b>0) return pow(static_cast(a),static_cast(b),rnd_mode); //mpfr_ui_pow_ui + else return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + }else{ + return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si + } } inline const mpreal pow(const int a, const long double b, mp_rnd_t rnd_mode) { - if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow + if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow } inline const mpreal pow(const int a, const double b, mp_rnd_t rnd_mode) { - if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow - else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow + if (a>=0) return pow(static_cast(a),mpreal(b),rnd_mode); //mpfr_ui_pow + else return pow(mpreal(a),mpreal(b),rnd_mode); //mpfr_pow } // pow long double inline const mpreal pow(const long double a, const long double b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),mpreal(b),rnd_mode); + return pow(mpreal(a),mpreal(b),rnd_mode); } inline const mpreal pow(const long double a, const unsigned long int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),b,rnd_mode); //mpfr_pow_ui + return pow(mpreal(a),b,rnd_mode); //mpfr_pow_ui } inline const mpreal pow(const long double a, const unsigned int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),static_cast(b),rnd_mode); //mpfr_pow_ui + return pow(mpreal(a),static_cast(b),rnd_mode); //mpfr_pow_ui } inline const mpreal pow(const long double a, const long int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si + return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si } inline const mpreal pow(const long double a, const int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si + return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si } inline const mpreal pow(const double a, const double b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),mpreal(b),rnd_mode); + return pow(mpreal(a),mpreal(b),rnd_mode); } inline const mpreal pow(const double a, const unsigned long int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),b,rnd_mode); // mpfr_pow_ui + return pow(mpreal(a),b,rnd_mode); // mpfr_pow_ui } inline const mpreal pow(const double a, const unsigned int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_ui + return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_ui } inline const mpreal pow(const double a, const long int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si + return pow(mpreal(a),b,rnd_mode); // mpfr_pow_si } inline const mpreal pow(const double a, const int b, mp_rnd_t rnd_mode) { - return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si + return pow(mpreal(a),static_cast(b),rnd_mode); // mpfr_pow_si } } // End of mpfr namespace @@ -3005,122 +3003,122 @@ inline const mpreal pow(const double a, const int b, mp_rnd_t rnd_mode) namespace std { // we are allowed to extend namespace std with specializations only - template <> - inline void swap(mpfr::mpreal& x, mpfr::mpreal& y) - { - return mpfr::swap(x, y); - } - - template<> - class numeric_limits - { - public: - static const bool is_specialized = true; - static const bool is_signed = true; - static const bool is_integer = false; - static const bool is_exact = false; - static const int radix = 2; - - static const bool has_infinity = true; - static const bool has_quiet_NaN = true; - static const bool has_signaling_NaN = true; - - static const bool is_iec559 = true; // = IEEE 754 - static const bool is_bounded = true; - static const bool is_modulo = false; - static const bool traps = true; - static const bool tinyness_before = true; - - static const float_denorm_style has_denorm = denorm_absent; - - inline static mpfr::mpreal (min) (mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return mpfr::minval(precision); } - inline static mpfr::mpreal (max) (mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return mpfr::maxval(precision); } - inline static mpfr::mpreal lowest (mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return -mpfr::maxval(precision); } - - // Returns smallest eps such that 1 + eps != 1 (classic machine epsilon) - inline static mpfr::mpreal epsilon(mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return mpfr::machine_epsilon(precision); } - - // Returns smallest eps such that x + eps != x (relative machine epsilon) - inline static mpfr::mpreal epsilon(const mpfr::mpreal& x) { return mpfr::machine_epsilon(x); } - - inline static mpfr::mpreal round_error(mp_prec_t precision = mpfr::mpreal::get_default_prec()) - { - mp_rnd_t r = mpfr::mpreal::get_default_rnd(); - - if(r == GMP_RNDN) return mpfr::mpreal(0.5, precision); - else return mpfr::mpreal(1.0, precision); - } - - inline static const mpfr::mpreal infinity() { return mpfr::const_infinity(); } - inline static const mpfr::mpreal quiet_NaN() { return mpfr::mpreal().setNan(); } - inline static const mpfr::mpreal signaling_NaN() { return mpfr::mpreal().setNan(); } - inline static const mpfr::mpreal denorm_min() { return (min)(); } - - // Please note, exponent range is not fixed in MPFR - static const int min_exponent = MPFR_EMIN_DEFAULT; - static const int max_exponent = MPFR_EMAX_DEFAULT; - MPREAL_PERMISSIVE_EXPR static const int min_exponent10 = (int) (MPFR_EMIN_DEFAULT * 0.3010299956639811); - MPREAL_PERMISSIVE_EXPR static const int max_exponent10 = (int) (MPFR_EMAX_DEFAULT * 0.3010299956639811); + template <> + inline void swap(mpfr::mpreal& x, mpfr::mpreal& y) + { + return mpfr::swap(x, y); + } + + template<> + class numeric_limits + { + public: + static const bool is_specialized = true; + static const bool is_signed = true; + static const bool is_integer = false; + static const bool is_exact = false; + static const int radix = 2; + + static const bool has_infinity = true; + static const bool has_quiet_NaN = true; + static const bool has_signaling_NaN = true; + + static const bool is_iec559 = true; // = IEEE 754 + static const bool is_bounded = true; + static const bool is_modulo = false; + static const bool traps = true; + static const bool tinyness_before = true; + + static const float_denorm_style has_denorm = denorm_absent; + + inline static mpfr::mpreal (min) (mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return mpfr::minval(precision); } + inline static mpfr::mpreal (max) (mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return mpfr::maxval(precision); } + inline static mpfr::mpreal lowest (mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return -mpfr::maxval(precision); } + + // Returns smallest eps such that 1 + eps != 1 (classic machine epsilon) + inline static mpfr::mpreal epsilon(mp_prec_t precision = mpfr::mpreal::get_default_prec()) { return mpfr::machine_epsilon(precision); } + + // Returns smallest eps such that x + eps != x (relative machine epsilon) + inline static mpfr::mpreal epsilon(const mpfr::mpreal& x) { return mpfr::machine_epsilon(x); } + + inline static mpfr::mpreal round_error(mp_prec_t precision = mpfr::mpreal::get_default_prec()) + { + mp_rnd_t r = mpfr::mpreal::get_default_rnd(); + + if(r == GMP_RNDN) return mpfr::mpreal(0.5, precision); + else return mpfr::mpreal(1.0, precision); + } + + inline static const mpfr::mpreal infinity() { return mpfr::const_infinity(); } + inline static const mpfr::mpreal quiet_NaN() { return mpfr::mpreal().setNan(); } + inline static const mpfr::mpreal signaling_NaN() { return mpfr::mpreal().setNan(); } + inline static const mpfr::mpreal denorm_min() { return (min)(); } + + // Please note, exponent range is not fixed in MPFR + static const int min_exponent = MPFR_EMIN_DEFAULT; + static const int max_exponent = MPFR_EMAX_DEFAULT; + MPREAL_PERMISSIVE_EXPR static const int min_exponent10 = (int) (MPFR_EMIN_DEFAULT * 0.3010299956639811); + MPREAL_PERMISSIVE_EXPR static const int max_exponent10 = (int) (MPFR_EMAX_DEFAULT * 0.3010299956639811); #ifdef MPREAL_HAVE_DYNAMIC_STD_NUMERIC_LIMITS - // Following members should be constant according to standard, but they can be variable in MPFR - // So we define them as functions here. - // - // This is preferable way for std::numeric_limits specialization. - // But it is incompatible with standard std::numeric_limits and might not work with other libraries, e.g. boost. - // See below for compatible implementation. - inline static float_round_style round_style() - { - mp_rnd_t r = mpfr::mpreal::get_default_rnd(); - - switch (r) - { - case GMP_RNDN: return round_to_nearest; - case GMP_RNDZ: return round_toward_zero; - case GMP_RNDU: return round_toward_infinity; - case GMP_RNDD: return round_toward_neg_infinity; - default: return round_indeterminate; - } - } - - inline static int digits() { return int(mpfr::mpreal::get_default_prec()); } - inline static int digits(const mpfr::mpreal& x) { return x.getPrecision(); } - - inline static int digits10(mp_prec_t precision = mpfr::mpreal::get_default_prec()) - { - return mpfr::bits2digits(precision); - } - - inline static int digits10(const mpfr::mpreal& x) - { - return mpfr::bits2digits(x.getPrecision()); - } - - inline static int max_digits10(mp_prec_t precision = mpfr::mpreal::get_default_prec()) - { - return digits10(precision); - } + // Following members should be constant according to standard, but they can be variable in MPFR + // So we define them as functions here. + // + // This is preferable way for std::numeric_limits specialization. + // But it is incompatible with standard std::numeric_limits and might not work with other libraries, e.g. boost. + // See below for compatible implementation. + inline static float_round_style round_style() + { + mp_rnd_t r = mpfr::mpreal::get_default_rnd(); + + switch (r) + { + case GMP_RNDN: return round_to_nearest; + case GMP_RNDZ: return round_toward_zero; + case GMP_RNDU: return round_toward_infinity; + case GMP_RNDD: return round_toward_neg_infinity; + default: return round_indeterminate; + } + } + + inline static int digits() { return int(mpfr::mpreal::get_default_prec()); } + inline static int digits(const mpfr::mpreal& x) { return x.getPrecision(); } + + inline static int digits10(mp_prec_t precision = mpfr::mpreal::get_default_prec()) + { + return mpfr::bits2digits(precision); + } + + inline static int digits10(const mpfr::mpreal& x) + { + return mpfr::bits2digits(x.getPrecision()); + } + + inline static int max_digits10(mp_prec_t precision = mpfr::mpreal::get_default_prec()) + { + return digits10(precision); + } #else - // Digits and round_style are NOT constants when it comes to mpreal. - // If possible, please use functions digits() and round_style() defined above. - // - // These (default) values are preserved for compatibility with existing libraries, e.g. boost. - // Change them accordingly to your application. - // - // For example, if you use 256 bits of precision uniformly in your program, then: - // digits = 256 - // digits10 = 77 - // max_digits10 = 78 - // - // Approximate formula for decimal digits is: digits10 = floor(log10(2) * digits). See bits2digits() for more details. - - static const std::float_round_style round_style = round_to_nearest; - static const int digits = 53; - static const int digits10 = 15; - static const int max_digits10 = 16; + // Digits and round_style are NOT constants when it comes to mpreal. + // If possible, please use functions digits() and round_style() defined above. + // + // These (default) values are preserved for compatibility with existing libraries, e.g. boost. + // Change them accordingly to your application. + // + // For example, if you use 256 bits of precision uniformly in your program, then: + // digits = 256 + // digits10 = 77 + // max_digits10 = 78 + // + // Approximate formula for decimal digits is: digits10 = floor(log10(2) * digits). See bits2digits() for more details. + + static const std::float_round_style round_style = round_to_nearest; + static const int digits = 53; + static const int digits10 = 15; + static const int max_digits10 = 16; #endif - }; + }; } diff --git a/makefile b/makefile index e7af9490..cc9c0b9d 100644 --- a/makefile +++ b/makefile @@ -7,7 +7,7 @@ WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wno-deprecated-declarations -CLANGWARNINGS := -Wno-undefined-func-template -Wno-comma -Wno-nullability-completeness -Wno-redundant-move -Wno-nested-anon-types -Wno-gnu-anonymous-struct -Wno-reserved-id-macro -Wno-extra-semi -Wno-gnu-zero-variadic-macro-arguments -Wno-shift-sign-overflow -Wno-exit-time-destructors -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-documentation-unknown-command -Wno-weak-vtables -Wno-c++98-compat +CLANGWARNINGS := -Wno-undefined-func-template -Wno-comma -Wno-nullability-completeness -Wno-redundant-move -Wno-nested-anon-types -Wno-gnu-anonymous-struct -Wno-reserved-id-macro -Wno-extra-semi -Wno-gnu-zero-variadic-macro-arguments -Wno-shift-sign-overflow -Wno-exit-time-destructors -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-documentation-unknown-command -Wno-weak-vtables -Wno-c++98-compat -Wold-style-cast SYSROOT := build/sysroot @@ -41,7 +41,7 @@ NUMFILES := $$(($(words $(CXXSRC)) + $(words $(CSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fexceptions -fno-omit-frame-pointer -Wno-old-style-cast $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fexceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) CFLAGS += -std=c11 -O0 -g -c -Wall -fno-omit-frame-pointer -Wno-overlength-strings $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) diff --git a/source/backend/interp/driver.cpp b/source/backend/interp/driver.cpp index ce13d09c..3186d32d 100644 --- a/source/backend/interp/driver.cpp +++ b/source/backend/interp/driver.cpp @@ -23,7 +23,7 @@ namespace backend if(frontend::getPrintProfileStats()) { auto dur = std::chrono::high_resolution_clock::now() - ts; - auto ms = (double) dur.count() / 1000.0 / 1000.0; + auto ms = static_cast(dur.count()) / 1000000.0; printf("%s took %.1f ms%s\n", thing.c_str(), ms, ms > 3000 ? strprintf(" (aka %.2f s)", ms / 1000.0).c_str() : ""); } } diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index 585fc0d6..757f3d9d 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -19,6 +19,9 @@ #pragma warning(push, 0) #pragma warning(disable: 4267) #pragma warning(disable: 4244) +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" #endif #include "llvm/IR/Verifier.h" @@ -45,6 +48,8 @@ #ifdef _MSC_VER #pragma warning(pop) +#else + #pragma GCC diagnostic pop #endif #include @@ -72,7 +77,7 @@ static void _printTiming(T ts, const std::string& thing) if(frontend::getPrintProfileStats()) { auto dur = std::chrono::high_resolution_clock::now() - ts; - auto ms = (double) dur.count() / 1000.0 / 1000.0; + auto ms = static_cast(dur.count()) / 1000000.0; printf("%s took %.1f ms%s\n", thing.c_str(), ms, ms > 3000 ? strprintf(" (aka %.2f s)", ms / 1000.0).c_str() : ""); } } @@ -218,7 +223,7 @@ namespace backend else if(frontend::getOutputMode() == ProgOutputMode::LLVMBitcode) { std::error_code e; - llvm::sys::fs::OpenFlags of = (llvm::sys::fs::OpenFlags) 0; + llvm::sys::fs::OpenFlags of = static_cast(0); llvm::raw_fd_ostream rso(oname.c_str(), e, of); llvm::WriteBitcodeToFile(*this->linkedModule.get(), rso); @@ -438,9 +443,8 @@ namespace backend this->jitInstance = new LLVMJit(this->targetMachine); this->jitInstance->addModule(std::move(this->linkedModule)); - // this->jitInstance-> auto entryaddr = this->jitInstance->getSymbolAddress(name); - ret = (EntryPoint_t) entryaddr; + ret = reinterpret_cast(entryaddr); iceAssert(ret && "failed to resolve entry function address"); } diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index c51e6071..d8e1abd5 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -13,6 +13,9 @@ #ifdef _MSC_VER #pragma warning(push, 0) +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" #endif #include "llvm/IR/Verifier.h" @@ -21,6 +24,8 @@ #ifdef _MSC_VER #pragma warning(pop) +#else + #pragma GCC diagnostic pop #endif #include "gluecode.h" @@ -71,7 +76,7 @@ namespace backend static llvm::Type* getNativeWordTy() { auto& gc = LLVMBackend::getLLVMContext(); - return llvm::IntegerType::getIntNTy(gc, (unsigned int) fir::getNativeWordSizeInBits()); + return llvm::IntegerType::getIntNTy(gc, static_cast(fir::getNativeWordSizeInBits())); } static llvm::Type* typeToLlvm(fir::Type* type, llvm::Module* mod) @@ -84,7 +89,7 @@ namespace backend // signed/unsigned is lost. if(pt->isIntegerType()) { - return llvm::IntegerType::getIntNTy(gc, (unsigned int) pt->getIntegerBitWidth()); + return llvm::IntegerType::getIntNTy(gc, static_cast(pt->getIntegerBitWidth())); } else if(pt->isFloatingPointType()) { @@ -281,7 +286,7 @@ namespace backend for(auto v : ut->getVariants()) { if(!v.second->getInteriorType()->isVoidType()) - maxSz = std::max(maxSz, (size_t) dl.getTypeAllocSize(typeToLlvm(v.second->getInteriorType(), mod))); + maxSz = std::max(maxSz, static_cast(dl.getTypeAllocSize(typeToLlvm(v.second->getInteriorType(), mod)))); } if(maxSz > 0) @@ -308,11 +313,11 @@ namespace backend size_t maxSz = 0; for(auto v : ut->getVariants()) - maxSz = std::max(maxSz, (size_t) dl.getTypeAllocSize(typeToLlvm(v.second, mod))); + maxSz = std::max(maxSz, static_cast(dl.getTypeAllocSize(typeToLlvm(v.second, mod)))); iceAssert(maxSz > 0); createdTypes[ut->getTypeName()] = llvm::StructType::create(gc, { - llvm::IntegerType::getIntNTy(gc, (unsigned int) (maxSz * CHAR_BIT)) + llvm::IntegerType::getIntNTy(gc, static_cast(maxSz * CHAR_BIT)) }, ut->getTypeName().mangled()); return createdTypes[ut->getTypeName()]; @@ -1526,10 +1531,14 @@ namespace backend auto phi = dcast(fir::PHINode, inst->realOutput); iceAssert(phi); - llvm::PHINode* ret = builder.CreatePHI(t, (unsigned int) phi->getValues().size()); + llvm::PHINode* ret = builder.CreatePHI(t, static_cast(phi->getValues().size())); for(auto v : phi->getValues()) - ret->addIncoming(decay(v.second, getValue(v.second)), llvm::cast(decay(v.first, getValue(v.first)))); + { + ret->addIncoming(decay(v.second, getValue(v.second)), + llvm::cast(decay(v.first, getValue(v.first))) + ); + } addValueToMap(ret, inst->realOutput); break; @@ -1604,7 +1613,7 @@ namespace backend clsty->getVirtualMethodCount())->getPointerTo()); auto fptr = builder.CreateConstInBoundsGEP2_32(vtable->getType()->getPointerElementType(), vtable, - 0, (unsigned int) dcast(fir::ConstantInt, inst->operands[1])->getUnsignedValue()); + 0, static_cast(dcast(fir::ConstantInt, inst->operands[1])->getUnsignedValue())); auto ffty = inst->operands[2]->getType()->toFunctionType(); @@ -1863,7 +1872,7 @@ namespace backend fir::ConstantInt* ci = dcast(fir::ConstantInt, inst->operands[i]); iceAssert(ci); - inds.push_back((unsigned int) ci->getUnsignedValue()); + inds.push_back(static_cast(ci->getUnsignedValue())); } @@ -1899,7 +1908,7 @@ namespace backend fir::ConstantInt* ci = dcast(fir::ConstantInt, inst->operands[i]); iceAssert(ci); - inds.push_back((unsigned int) ci->getUnsignedValue()); + inds.push_back(static_cast(ci->getUnsignedValue())); } iceAssert(str->getType()->isStructTy() || str->getType()->isArrayTy()); @@ -2282,7 +2291,7 @@ namespace backend iceAssert(ci); llvm::Value* ret = builder.CreateStructGEP(ptr->getType()->getPointerElementType(), - ptr, (unsigned int) ci->getUnsignedValue()); + ptr, static_cast(ci->getUnsignedValue())); addValueToMap(ret, inst->realOutput); break; @@ -2323,7 +2332,7 @@ namespace backend auto ut = inst->operands[0]->getType()->toUnionType(); auto vid = dcast(fir::ConstantInt, inst->operands[1])->getSignedValue(); - iceAssert((size_t) vid < ut->getVariantCount()); + iceAssert(static_cast(vid < ut->getVariantCount())); auto vt = ut->getVariant(vid)->getInteriorType(); auto lut = typeToLlvm(ut, module); diff --git a/source/codegen/alloc.cpp b/source/codegen/alloc.cpp index 77b94633..8a34ef16 100644 --- a/source/codegen/alloc.cpp +++ b/source/codegen/alloc.cpp @@ -17,7 +17,8 @@ static fir::Function* getCheckNegativeLengthFunction(cgn::CodegenState* cs) auto restore = cs->irb.getCurrentBlock(); fir::Function* func = cs->module->getOrCreateFunction(fname, - fir::FunctionType::get({ fir::Type::getNativeWord(), fir::Type::getCharSlice(false) }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::FunctionType::get({ fir::Type::getNativeWord(), fir::Type::getCharSlice(false) }, fir::Type::getVoid()), + fir::LinkageType::Internal); func->setAlwaysInline(); diff --git a/source/codegen/arithmetic.cpp b/source/codegen/arithmetic.cpp index b4120deb..1b1d4fde 100644 --- a/source/codegen/arithmetic.cpp +++ b/source/codegen/arithmetic.cpp @@ -49,7 +49,7 @@ namespace sst if(auto parent = target->toUnionVariantType()->getParentUnion(); parent != vt) { error(this, "unwrapping union of type '%s' to variant ('%s') of unrelated union '%s'", - vt, target->toUnionVariantType()->getName(), (fir::Type*) parent); + vt, target->toUnionVariantType()->getName(), dcast(fir::Type, parent)); } else { diff --git a/source/codegen/constructor.cpp b/source/codegen/constructor.cpp index 187ba9fe..55f74232 100644 --- a/source/codegen/constructor.cpp +++ b/source/codegen/constructor.cpp @@ -118,7 +118,7 @@ fir::Value* cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, if(vargs.size() != wrapper_func->getArgumentCount()) { SimpleError::make(this->loc(), "mismatched number of arguments in constructor call to class '%s'; expected %d, found %d instead", - (fir::Type*) cls, constrfn->getArgumentCount(), vargs.size()) + dcast(fir::Type, cls), constrfn->getArgumentCount(), vargs.size()) ->append(SimpleError::make(MsgType::Note, constr->loc, "constructor was defined here:")) ->postAndQuit(); } diff --git a/source/codegen/function.cpp b/source/codegen/function.cpp index 70b1b60b..6633d841 100644 --- a/source/codegen/function.cpp +++ b/source/codegen/function.cpp @@ -17,8 +17,6 @@ CGResult sst::FunctionDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) return CGResult(0); std::vector ptypes; - // if(this->parentTypeForMethod) - // ptypes.push_back(this->isMutating ? this->parentTypeForMethod->getMutablePointerTo() : this->parentTypeForMethod->getPointerTo()); for(auto p : this->params) ptypes.push_back(p.type); diff --git a/source/codegen/literals.cpp b/source/codegen/literals.cpp index ac49d206..60e28157 100644 --- a/source/codegen/literals.cpp +++ b/source/codegen/literals.cpp @@ -204,7 +204,7 @@ CGResult sst::LiteralTuple::_codegen(cgn::CodegenState* cs, fir::Type* infer) i, ty, vr->getType()); } - allConst &= (bool) dcast(fir::ConstantValue, vr); + allConst &= (dcast(fir::ConstantValue, vr) != nullptr); vals.push_back(vr); } @@ -252,7 +252,7 @@ CGResult sst::LiteralChar::_codegen(cgn::CodegenState* cs, fir::Type* infer) cs->pushLoc(this); defer(cs->popLoc()); - return CGResult(fir::ConstantInt::getInt8((int8_t) this->value)); + return CGResult(fir::ConstantInt::getInt8(static_cast(this->value))); } CGResult sst::LiteralString::_codegen(cgn::CodegenState* cs, fir::Type* infer) diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 0ef5f29b..2a5bccb6 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -87,7 +87,7 @@ namespace fir int64_t ConstantInt::getSignedValue() { - return (int64_t) this->value; + return static_cast(this->value); } uint64_t ConstantInt::getUnsignedValue() @@ -187,7 +187,7 @@ namespace fir ConstantFP::ConstantFP(Type* type, float val) : fir::ConstantValue(type) { - this->value = (double) val; + this->value = static_cast(val); } ConstantFP::ConstantFP(Type* type, double val) : fir::ConstantValue(type) diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 228e50b0..e0648b94 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -13,9 +13,6 @@ #include "memorypool.h" -#define DO_IN_SITU_CONSTANT_FOLDING 0 - - static bool isSAAType(fir::Type* t) { @@ -427,7 +424,7 @@ namespace fir { iceAssert(v->getType()->isFloatingPointType() && targetType->isFloatingPointType() && "not floating point type"); Instruction* instr = make_instr(OpKind::Floating_Truncate, false, targetType, - { v, ConstantValue::getZeroValue(targetType) }); + util::vectorOf(v, ConstantValue::getZeroValue(targetType))); return this->addInstruction(instr, vname); } @@ -763,7 +760,7 @@ namespace fir if(std::modf(cfp->getValue(), &_) != 0.0) warn("truncating constant '%Lf' in constant cast to type '%s'", cfp->getValue(), targetType); - return ConstantInt::get(targetType, (size_t) cfp->getValue()); + return ConstantInt::get(targetType, static_cast(cfp->getValue())); } Instruction* instr = make_instr(OpKind::Cast_FloatToInt, false, targetType, @@ -784,13 +781,13 @@ namespace fir bool sgn = ci->getType()->isSignedIntType(); if(targetType == Type::getFloat32()) { - if(sgn) ret = ConstantFP::getFloat32((float) ci->getSignedValue()); - else ret = ConstantFP::getFloat32((float) ci->getUnsignedValue()); + if(sgn) ret = ConstantFP::getFloat32(static_cast(ci->getSignedValue())); + else ret = ConstantFP::getFloat32(static_cast(ci->getUnsignedValue())); } else if(targetType == Type::getFloat64()) { - if(sgn) ret = ConstantFP::getFloat64((double) ci->getSignedValue()); - else ret = ConstantFP::getFloat64((double) ci->getUnsignedValue()); + if(sgn) ret = ConstantFP::getFloat64(static_cast(ci->getSignedValue())); + else ret = ConstantFP::getFloat64(static_cast(ci->getUnsignedValue())); } else { @@ -1088,7 +1085,14 @@ namespace fir iceAssert(self && self == cls); Instruction* instr = make_instr(OpKind::Value_CallVirtualMethod, true, ft->getReturnType(), - (Value*) ConstantValue::getZeroValue(cls) + ((Value*) ConstantInt::getNative(index) + ((Value*) ConstantValue::getZeroValue(ft) + args))); + util::vectorOf( + ConstantValue::getZeroValue(cls), + ConstantInt::getNative(index), + ConstantValue::getZeroValue(ft)) + args + ); + + + // (Value*) ConstantValue::getZeroValue(cls) + ((Value*) ConstantInt::getNative(index) + ((Value*) ConstantValue::getZeroValue(ft) + args))); return this->addInstruction(instr, vname); } @@ -1148,7 +1152,7 @@ namespace fir // insert at the front (back = no guarantees) this->currentBlock->instructions.insert(this->currentBlock->instructions.begin(), instr); - return (PHINode*) instr->realOutput; + return dcast(PHINode, instr->realOutput); } Value* IRBuilder::StackAlloc(Type* type, const std::string& vname) diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 5c6ada21..0f343d67 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -410,7 +410,7 @@ namespace fir else { error("no method named '%s' matching signature '%s' in virtual method table of class '%s'", - name, (Type*) ft, this->getTypeName().name); + name, dcast(Type, ft), this->getTypeName().name); } } diff --git a/source/fir/Types/SingleTypes.cpp b/source/fir/Types/SingleTypes.cpp index 22b001de..1944100d 100644 --- a/source/fir/Types/SingleTypes.cpp +++ b/source/fir/Types/SingleTypes.cpp @@ -113,7 +113,7 @@ namespace fir if(cnt->isFloating()) { if(cnt->getMinBits() > 64) - error("constant number type '%s' requires too many bits", (Type*) cnt); + error("constant number type '%s' requires too many bits", dcast(Type, cnt)); return fir::Type::getFloat64(); } @@ -125,12 +125,12 @@ namespace fir } else if(cnt->isSigned()) { - error("constant number type '%s' requires too many bits", (Type*) cnt); + error("constant number type '%s' requires too many bits", dcast(Type, cnt)); } else { if(cnt->getMinBits() > fir::Type::getNativeUWord()->getBitWidth()) - error("constant number type '%s' requires too many bits", (Type*) cnt); + error("constant number type '%s' requires too many bits", dcast(Type, cnt)); return fir::Type::getNativeUWord(); } @@ -154,7 +154,7 @@ namespace fir static std::vector> cache; PolyPlaceholderType* PolyPlaceholderType::get(const std::string& n, int group) { - while((size_t) group >= cache.size()) + while(static_cast(group) >= cache.size()) cache.push_back({ }); if(auto it = cache[group].find(n); it != cache[group].end()) diff --git a/source/fir/Types/Type.cpp b/source/fir/Types/Type.cpp index d4b69f45..41605a61 100644 --- a/source/fir/Types/Type.cpp +++ b/source/fir/Types/Type.cpp @@ -61,7 +61,9 @@ namespace fir { if(from->isSignedIntType() == to->isSignedIntType()) { - auto bitdiff = abs((int) from->toPrimitiveType()->getIntegerBitWidth() - (int) to->toPrimitiveType()->getIntegerBitWidth()); + auto bitdiff = std::abs(static_cast(from->toPrimitiveType()->getIntegerBitWidth()) + - static_cast(to->toPrimitiveType()->getIntegerBitWidth())); + switch(bitdiff) { case 0: return 0; // same diff --git a/source/fir/interp/compiler.cpp b/source/fir/interp/compiler.cpp index c60bb2d0..b856d2e9 100644 --- a/source/fir/interp/compiler.cpp +++ b/source/fir/interp/compiler.cpp @@ -19,7 +19,7 @@ namespace interp interp::Instruction ret; ret.result = finstr->realOutput; - ret.opcode = (uint64_t) finstr->opKind; + ret.opcode = static_cast(finstr->opKind); for(auto a : finstr->operands) ret.args.push_back(a); diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 465e3ccc..69172904 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -117,11 +117,11 @@ namespace interp { if(v.dataSize > LARGE_DATA_SIZE) { - return *((T*) v.ptr); + return *(static_cast(v.ptr)); } else { - return *((T*) &v.data[0]); + return *(reinterpret_cast(const_cast(&v.data[0]))); } } @@ -186,7 +186,7 @@ namespace interp static interp::Value loadFromPtr(const interp::Value& x, fir::Type* ty) { - auto ptr = (void*) getActualValue(x); + auto ptr = reinterpret_cast(getActualValue(x)); interp::Value ret; ret.dataSize = getSizeOfType(ty); @@ -253,14 +253,14 @@ namespace interp { interp::Value ret; - if(ci->getType() == fir::Type::getInt8()) ret = makeValue(c, (int8_t) ci->getSignedValue()); - else if(ci->getType() == fir::Type::getInt16()) ret = makeValue(c, (int16_t) ci->getSignedValue()); - else if(ci->getType() == fir::Type::getInt32()) ret = makeValue(c, (int32_t) ci->getSignedValue()); - else if(ci->getType() == fir::Type::getInt64()) ret = makeValue(c, (int64_t) ci->getSignedValue()); - else if(ci->getType() == fir::Type::getUint8()) ret = makeValue(c, (uint8_t) ci->getUnsignedValue()); - else if(ci->getType() == fir::Type::getUint16()) ret = makeValue(c, (uint16_t) ci->getUnsignedValue()); - else if(ci->getType() == fir::Type::getUint32()) ret = makeValue(c, (uint32_t) ci->getUnsignedValue()); - else if(ci->getType() == fir::Type::getUint64()) ret = makeValue(c, (uint64_t) ci->getUnsignedValue()); + if(ci->getType() == fir::Type::getInt8()) ret = makeValue(c, static_cast(ci->getSignedValue())); + else if(ci->getType() == fir::Type::getInt16()) ret = makeValue(c, static_cast(ci->getSignedValue())); + else if(ci->getType() == fir::Type::getInt32()) ret = makeValue(c, static_cast(ci->getSignedValue())); + else if(ci->getType() == fir::Type::getInt64()) ret = makeValue(c, static_cast(ci->getSignedValue())); + else if(ci->getType() == fir::Type::getUint8()) ret = makeValue(c, static_cast(ci->getUnsignedValue())); + else if(ci->getType() == fir::Type::getUint16()) ret = makeValue(c, static_cast(ci->getUnsignedValue())); + else if(ci->getType() == fir::Type::getUint32()) ret = makeValue(c, static_cast(ci->getUnsignedValue())); + else if(ci->getType() == fir::Type::getUint64()) ret = makeValue(c, static_cast(ci->getUnsignedValue())); else error("interp: unsupported type '%s' for integer constant", ci->getType()); return (cachedConstants[c] = ret); @@ -341,7 +341,7 @@ namespace interp void* buffer = new uint8_t[sz]; memset(buffer, 0, sz); is->globalAllocs.push_back(buffer); - uint8_t* ofs = (uint8_t*) buffer; + uint8_t* ofs = reinterpret_cast(buffer); for(const auto& x : theArray->getValues()) { auto v = makeConstant(is, x); @@ -391,7 +391,7 @@ namespace interp // make sure we compile it first, so it gets added to InterpState::compiledFunctions is->compileFunction(fn); - auto ret = makeValue(fn, (uintptr_t) fn); + auto ret = makeValue(fn, reinterpret_cast(fn)); return (cachedConstants[c] = ret); } else if(auto glob = dcast(fir::GlobalValue, c)) @@ -660,14 +660,14 @@ namespace interp } uintptr_t dst = 0; - if(str.dataSize > LARGE_DATA_SIZE) dst = (uintptr_t) ret.ptr; - else dst = (uintptr_t) &ret.data[0]; + if(str.dataSize > LARGE_DATA_SIZE) dst = reinterpret_cast(ret.ptr); + else dst = reinterpret_cast(&ret.data[0]); uintptr_t src = 0; - if(elm.dataSize > LARGE_DATA_SIZE) src = (uintptr_t) elm.ptr; - else src = (uintptr_t) &elm.data[0]; + if(elm.dataSize > LARGE_DATA_SIZE) src = reinterpret_cast(elm.ptr); + else src = reinterpret_cast(&elm.data[0]); - memmove((void*) (dst + ofs), (void*) src, elm.dataSize); + memmove(reinterpret_cast(dst + ofs), reinterpret_cast(src), elm.dataSize); return ret; } @@ -709,14 +709,14 @@ namespace interp iceAssert(ret.type == elm); uintptr_t src = 0; - if(str.dataSize > LARGE_DATA_SIZE) src = (uintptr_t) str.ptr; - else src = (uintptr_t) &str.data[0]; + if(str.dataSize > LARGE_DATA_SIZE) src = reinterpret_cast(str.ptr); + else src = reinterpret_cast(&str.data[0]); uintptr_t dst = 0; - if(ret.dataSize > LARGE_DATA_SIZE) dst = (uintptr_t) ret.ptr; - else dst = (uintptr_t) &ret.data[0]; + if(ret.dataSize > LARGE_DATA_SIZE) dst = reinterpret_cast(ret.ptr); + else dst = reinterpret_cast(&ret.data[0]); - memmove((void*) dst, (void*) (src + ofs), ret.dataSize); + memmove(reinterpret_cast(dst), reinterpret_cast(src + ofs), ret.dataSize); return ret; } @@ -984,23 +984,23 @@ namespace interp for(size_t i = 0; i < args.size(); i++) { if(args[i].dataSize <= LARGE_DATA_SIZE) - arg_values[i] = (void*) &args[i].data[0]; + arg_values[i] = reinterpret_cast(const_cast(&args[i].data[0])); } for(size_t i = 0; i < args.size(); i++) { if(args[i].dataSize <= LARGE_DATA_SIZE) - arg_pointers[i] = (void*) arg_values[i]; + arg_pointers[i] = reinterpret_cast(arg_values[i]); else - arg_pointers[i] = (void*) args[i].ptr; + arg_pointers[i] = reinterpret_cast(args[i].ptr); } delete[] arg_values; } - void* ret_buffer = new uint8_t[std::max(ffi_retty->size, (size_t) 8)]; - ffi_call(&fn_cif, FFI_FN(fnptr), ret_buffer, arg_pointers); + void* ret_buffer = new uint8_t[std::max(ffi_retty->size, size_t(8))]; + ffi_call(&fn_cif, reinterpret_cast(fnptr), ret_buffer, arg_pointers); interp::Value ret; ret.type = fnty->getReturnType(); @@ -1132,7 +1132,7 @@ namespace interp case FLOW_DYCALL: { auto ptr = getActualValue(res.virtualCallTarget); - auto firfn = (fir::Function*) ptr; + auto firfn = reinterpret_cast(ptr); if(auto it = is->compiledFunctions.find(firfn); it != is->compiledFunctions.end()) { @@ -1156,8 +1156,9 @@ namespace interp else error("interp: call to function pointer with invalid type '%s'", targ.type); - is->stackFrames.back().values[res.callResultValue] = runFunctionWithLibFFI(is, (void*) ptr, fnty, res.callArguments, - /* name: */ res.callTarget->extFuncName); + is->stackFrames.back().values[res.callResultValue] = runFunctionWithLibFFI(is, reinterpret_cast(ptr), + fnty, res.callArguments, /* name: */ res.callTarget->extFuncName); + i += 1; } } break; @@ -1340,7 +1341,7 @@ namespace interp // returns either FLOW_NORMAL, FLOW_BRANCH or FLOW_RETURN. static int runInstruction(InterpState* is, const interp::Instruction& inst, InstrResult* instrRes) { - auto ok = (OpKind) inst.opcode; + auto ok = static_cast(inst.opcode); switch(ok) { case OpKind::Signed_Add: @@ -1646,10 +1647,10 @@ namespace interp interp::Value ret; if(a.type == Type::getFloat64() && t == Type::getFloat32()) - ret = makeValue(inst.result, (float) getActualValue(a)); + ret = makeValue(inst.result, static_cast(getActualValue(a))); - else if(a.type == Type::getFloat32()) ret = makeValue(inst.result, (float) getActualValue(a)); - else if(a.type == Type::getFloat64()) ret = makeValue(inst.result, (double) getActualValue(a)); + else if(a.type == Type::getFloat32()) ret = makeValue(inst.result, getActualValue(a)); + else if(a.type == Type::getFloat64()) ret = makeValue(inst.result, getActualValue(a)); else error("interp: unsupported"); setRet(is, inst, ret); @@ -1664,10 +1665,10 @@ namespace interp interp::Value ret; if(a.type == Type::getFloat32() && t == Type::getFloat64()) - ret = makeValue(inst.result, (double) getActualValue(a)); + ret = makeValue(inst.result, static_cast(getActualValue(a))); - else if(a.type == Type::getFloat32()) ret = makeValue(inst.result, (float) getActualValue(a)); - else if(a.type == Type::getFloat64()) ret = makeValue(inst.result, (double) getActualValue(a)); + else if(a.type == Type::getFloat32()) ret = makeValue(inst.result, getActualValue(a)); + else if(a.type == Type::getFloat64()) ret = makeValue(inst.result, getActualValue(a)); else error("interp: unsupported"); setRet(is, inst, ret); @@ -1686,7 +1687,7 @@ namespace interp if(a.type != b.type->getPointerElementType()) error("interp: cannot write '%s' into '%s'", a.type, b.type); - auto ptr = (void*) getActualValue(b); + auto ptr = reinterpret_cast(getActualValue(b)); if(a.dataSize > LARGE_DATA_SIZE) { // just a memcopy. @@ -1914,21 +1915,21 @@ namespace interp // just get the value as a bool, then cast it to an i64 ourselves, then // pass *that* to the ops. - auto boolval = (int64_t) getActualValue(a); + auto boolval = static_cast(getActualValue(a)); ret = oneArgumentOp(is, inst, b, [boolval](auto b) -> auto { - return (decltype(b)) boolval; + return static_cast(boolval); }); } else if(b.type->isBoolType()) { ret = oneArgumentOp(is, inst, a, [](auto a) -> auto { - return (bool) a; + return static_cast(a); }); } else { ret = twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> auto { - return (decltype(b)) a; + return static_cast(a); }); } @@ -1961,7 +1962,7 @@ namespace interp auto b = getArg(is, inst, 1); interp::Value ret = twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> auto { - return (decltype(b)) a; + return static_cast(a); }); setRet(is, inst, ret); @@ -1975,7 +1976,7 @@ namespace interp auto b = getArg(is, inst, 1); interp::Value ret = twoArgumentOp(is, inst, a, b, [](auto a, auto b) -> auto { - return (decltype(b)) a; + return static_cast(a); }); setRet(is, inst, ret); @@ -2053,10 +2054,10 @@ namespace interp auto ci = fir::ConstantInt::getNative(getSizeOfType(ty)); - if(fir::getNativeWordSizeInBits() == 64) setRet(is, inst, makeValue(inst.result, (int64_t) ci->getSignedValue())); - if(fir::getNativeWordSizeInBits() == 32) setRet(is, inst, makeValue(inst.result, (int32_t) ci->getSignedValue())); - if(fir::getNativeWordSizeInBits() == 16) setRet(is, inst, makeValue(inst.result, (int16_t) ci->getSignedValue())); - if(fir::getNativeWordSizeInBits() == 8) setRet(is, inst, makeValue(inst.result, (int8_t) ci->getSignedValue())); + if(fir::getNativeWordSizeInBits() == 64) setRet(is, inst, makeValue(inst.result, static_cast(ci->getSignedValue()))); + if(fir::getNativeWordSizeInBits() == 32) setRet(is, inst, makeValue(inst.result, static_cast(ci->getSignedValue()))); + if(fir::getNativeWordSizeInBits() == 16) setRet(is, inst, makeValue(inst.result, static_cast(ci->getSignedValue()))); + if(fir::getNativeWordSizeInBits() == 8) setRet(is, inst, makeValue(inst.result, static_cast(ci->getSignedValue()))); break; } @@ -2088,7 +2089,7 @@ namespace interp auto a = getArg(is, inst, 0); auto b = getUndecayedArg(is, inst, 1); - auto ptr = (void*) getActualValue(b); + auto ptr = reinterpret_cast(getActualValue(b)); if(a.dataSize > LARGE_DATA_SIZE) memmove(ptr, a.ptr, a.dataSize); else memmove(ptr, &a.data[0], a.dataSize); @@ -2142,7 +2143,7 @@ namespace interp auto str = getArg(is, inst, 0); auto elm = getArg(is, inst, 1); - auto idx = (size_t) getActualValue(getArg(is, inst, 2)); + auto idx = static_cast(getActualValue(getArg(is, inst, 2))); setRet(is, inst, doInsertValue(is, inst.result, str, elm, idx)); break; @@ -2154,7 +2155,7 @@ namespace interp iceAssert(inst.args.size() >= 2); auto str = getArg(is, inst, 0); - auto idx = (size_t) getActualValue(getArg(is, inst, 1)); + auto idx = static_cast(getActualValue(getArg(is, inst, 1))); setRet(is, inst, doExtractValue(is, inst.result, str, idx)); break; @@ -2414,7 +2415,7 @@ namespace interp auto ut = inst.args[0]->getType()->toUnionType(); auto vid = dcast(fir::ConstantInt, inst.args[1])->getSignedValue(); - iceAssert((size_t) vid < ut->getVariantCount()); + iceAssert(static_cast(vid) < ut->getVariantCount()); auto vt = ut->getVariant(vid)->getInteriorType(); // because we can operate with the raw memory values, we can probably do this a bit more efficiently @@ -2426,15 +2427,15 @@ namespace interp // then, get the array: uintptr_t arrayAddr = 0; - if(theUnion.dataSize > LARGE_DATA_SIZE) arrayAddr = (uintptr_t) theUnion.ptr; - else arrayAddr = (uintptr_t) &theUnion.data[0]; + if(theUnion.dataSize > LARGE_DATA_SIZE) arrayAddr = reinterpret_cast(theUnion.ptr); + else arrayAddr = reinterpret_cast(&theUnion.data[0]); // offset it appropriately: arrayAddr += getSizeOfType(fir::Type::getNativeWord()); // ok so now we just do a 'setRaw' to get the value out. auto ret = is->makeValue(inst.result); - setValueRaw(is, &ret, (void*) arrayAddr, getSizeOfType(vt)); + setValueRaw(is, &ret, reinterpret_cast(arrayAddr), getSizeOfType(vt)); setRet(is, inst, ret); break; @@ -2447,9 +2448,9 @@ namespace interp iceAssert(inst.args[0]->getType()->isUnionType()); auto ut = inst.args[0]->getType()->toUnionType(); - auto vid = (intptr_t) dcast(fir::ConstantInt, inst.args[1])->getSignedValue(); + auto vid = static_cast(dcast(fir::ConstantInt, inst.args[1])->getSignedValue()); - iceAssert((size_t) vid < ut->getVariantCount()); + iceAssert(static_cast(vid) < ut->getVariantCount()); // again, we do this "manually" because we can access the raw bytes, so we don't have to // twist ourselves through hoops like with llvm. @@ -2459,22 +2460,22 @@ namespace interp // then, get the array: uintptr_t baseAddr = 0; - if(theUnion.dataSize > LARGE_DATA_SIZE) baseAddr = (uintptr_t) theUnion.ptr; - else baseAddr = (uintptr_t) &theUnion.data[0]; + if(theUnion.dataSize > LARGE_DATA_SIZE) baseAddr = reinterpret_cast(theUnion.ptr); + else baseAddr = reinterpret_cast(&theUnion.data[0]); // offset it appropriately: auto arrayAddr = baseAddr + getSizeOfType(fir::Type::getNativeWord()); // ok, now we just do a memcpy into the struct. iceAssert(sizeof(intptr_t) == (fir::getNativeWordSizeInBits() / CHAR_BIT)); - memmove((void*) baseAddr, &vid, sizeof(intptr_t)); + memmove(reinterpret_cast(baseAddr), &vid, sizeof(intptr_t)); uintptr_t valueAddr = 0; auto theValue = getArg(is, inst, 2); - if(theValue.dataSize > LARGE_DATA_SIZE) valueAddr = (uintptr_t) theValue.ptr; - else valueAddr = (uintptr_t) &theValue.data[0]; + if(theValue.dataSize > LARGE_DATA_SIZE) valueAddr = reinterpret_cast(theValue.ptr); + else valueAddr = reinterpret_cast(&theValue.data[0]); - memmove((void*) arrayAddr, (void*) valueAddr, theValue.dataSize); + memmove(reinterpret_cast(arrayAddr), reinterpret_cast(valueAddr), theValue.dataSize); setRet(is, inst, theUnion); break; diff --git a/source/fir/interp/wrappers.cpp b/source/fir/interp/wrappers.cpp index 2d65a811..0e2d7d1d 100644 --- a/source/fir/interp/wrappers.cpp +++ b/source/fir/interp/wrappers.cpp @@ -92,7 +92,7 @@ PLATFORM_EXPORT_FUNCTION int __interp_wrapper_fprintf(void* stream, char* fmt, . va_list ap; va_start(ap, fmt); - int ret = vfprintf((FILE*) stream, fmt, ap); + int ret = vfprintf(static_cast(stream), fmt, ap); va_end(ap); return ret; diff --git a/source/frontend/errors.cpp b/source/frontend/errors.cpp index 0e367532..3c1cf363 100644 --- a/source/frontend/errors.cpp +++ b/source/frontend/errors.cpp @@ -221,7 +221,7 @@ static std::string typestr(MsgType t) template static size_t strprinterrf(const char* fmt, Ts... ts) { - return (size_t) fprintf(stderr, "%s", strprintf(fmt, ts...).c_str()); + return static_cast(fprintf(stderr, "%s", strprintf(fmt, ts...).c_str())); } // template @@ -341,7 +341,7 @@ void SpanError::post() this->spans.erase(std::find(this->spans.begin(), this->spans.end(), util::ESpan(this->top->loc, ""))); size_t cursor = 0; - size_t width = (size_t) (0.85 * platform::getTerminalWidth()); + size_t width = static_cast(0.85 * platform::getTerminalWidth()); // there's probably a more efficient way to do this, but since we're throwing an error and already going to die, // it doesn't really matter. diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index ecbc0ca6..affedd70 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -630,7 +630,7 @@ namespace lexer // because we want to avoid using std::string (ie. copying) in the lexer (Token), we must send the string over verbatim. // store the starting position - size_t start = (size_t) (stream.data() - whole.data() + 1); + size_t start = (stream.data() - whole.data() + 1); // opening " pos.col++; diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 1a1c739c..7618cbad 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -1250,7 +1250,7 @@ namespace parser case TT::CharacterLiteral: st.pop(); - return util::pool(tok.loc, (uint32_t) tok.text[0]); + return util::pool(tok.loc, static_cast(tok.text[0])); // no point creating separate functions for these case TT::True: diff --git a/source/frontend/parser/literal.cpp b/source/frontend/parser/literal.cpp index b606da95..7f04d4ae 100644 --- a/source/frontend/parser/literal.cpp +++ b/source/frontend/parser/literal.cpp @@ -39,7 +39,7 @@ namespace parser // ok then. char s[2] = { sv[1], sv[2] }; - char val = (char) std::stol(s, /* pos: */ 0, /* base: */ 16); + char val = static_cast(std::stol(s, /* pos: */ 0, /* base: */ 16)); *ofs = 3; return std::string(&val, 1); diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index be3b1206..5ee271e1 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -116,7 +116,7 @@ namespace parser bool isFirst = true; auto priv = VisibilityLevel::Invalid; - size_t tix = (size_t) -1; + size_t tix = static_cast(-1); while(st.hasTokens() && st.front() != TT::EndOfFile) @@ -160,7 +160,11 @@ namespace parser auto ns = parseTopLevel(st, tok.str()); if(priv != VisibilityLevel::Invalid) - ns->visibility = priv, priv = VisibilityLevel::Invalid, tix = (size_t) -1; + { + ns->visibility = priv; + priv = VisibilityLevel::Invalid; + tix = static_cast(-1); + } root->statements.push_back(ns); @@ -243,7 +247,7 @@ namespace parser { st.rewindTo(tix); - tix = (size_t) -1; + tix = static_cast(-1); priv = VisibilityLevel::Invalid; } diff --git a/source/include/backend.h b/source/include/backend.h index f7006722..ef74cba1 100644 --- a/source/include/backend.h +++ b/source/include/backend.h @@ -67,7 +67,7 @@ namespace backend struct Backend { - BackendCaps::Capabilities getCapabilities() { return (BackendCaps::Capabilities) this->capabilities; } + BackendCaps::Capabilities getCapabilities() { return static_cast(this->capabilities); } bool hasCapability(BackendCaps::Capabilities cap) { return this->capabilities & cap; } static Backend* getBackendFromOption(BackendOption opt, CompiledData& cd, std::vector in, std::string out); diff --git a/source/include/backends/llvm.h b/source/include/backends/llvm.h index d407eb22..c905a00e 100644 --- a/source/include/backends/llvm.h +++ b/source/include/backends/llvm.h @@ -14,8 +14,12 @@ #ifdef _MSC_VER #pragma warning(push, 0) +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" #endif + #include "llvm/IR/Mangler.h" #include "llvm/IR/DataLayout.h" @@ -38,6 +42,8 @@ #ifdef _MSC_VER #pragma warning(pop) +#else + #pragma GCC diagnostic pop #endif #include "backend.h" diff --git a/source/include/container.h b/source/include/container.h index 10a19709..c41beedb 100644 --- a/source/include/container.h +++ b/source/include/container.h @@ -31,7 +31,7 @@ namespace util for(auto c : other.chunks) { - auto nc = (ValueType*) mem::allocate_memory(sizeof(ValueType) * ChunkSize); + auto nc = static_cast(mem::allocate_memory(sizeof(ValueType) * ChunkSize)); memmove(nc, c, sizeof(ValueType) * ChunkSize); this->chunks.push_back(nc); } @@ -86,7 +86,7 @@ namespace util size_t offs = index % ChunkSize; iceAssert(cind < this->chunks.size()); - return *((ValueType*) (this->chunks[cind] + offs)); + return *(static_cast(this->chunks[cind] + offs)); } ValueType* getNextSlotAndIncrement() @@ -124,7 +124,7 @@ namespace util private: void makeNewChunk() { - this->chunks.push_back((ValueType*) mem::allocate_memory(sizeof(ValueType) * ChunkSize)); + this->chunks.push_back(static_cast(mem::allocate_memory(sizeof(ValueType) * ChunkSize))); } // possibly use a faster implementation?? since we're just storing pointers idk if there's a point. diff --git a/source/include/defs.h b/source/include/defs.h index c92c51b7..7b015c35 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -22,10 +22,10 @@ 1: experimental, std::experimental::string_view 2: external, stx::string_view */ -#if __has_include() && _HAS_CXX17 +#if __has_include() #include #define STRING_VIEW_TYPE 0 -#elif __has_include() && _HAS_CXX17 +#elif __has_include() #include #define STRING_VIEW_TYPE 1 #else diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 2ad9e265..5a6c4be3 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -4,7 +4,6 @@ #pragma once - #include #include #include @@ -38,14 +37,14 @@ namespace fir static ConstantNumber* get(ConstantNumberType* cnt, const mpfr::mpreal& n); - int8_t getInt8() { return (int8_t) this->number.toLLong(); } - int16_t getInt16() { return (int16_t) this->number.toLLong(); } - int32_t getInt32() { return (int32_t) this->number.toLLong(); } - int64_t getInt64() { return (int64_t) this->number.toLLong(); } - uint8_t getUint8() { return (uint8_t) this->number.toULLong(); } - uint16_t getUint16() { return (uint16_t) this->number.toULLong(); } - uint32_t getUint32() { return (uint32_t) this->number.toULLong(); } - uint64_t getUint64() { return (uint64_t) this->number.toULLong(); } + int8_t getInt8() { return static_cast(this->number.toLLong()); } + int16_t getInt16() { return static_cast(this->number.toLLong()); } + int32_t getInt32() { return static_cast(this->number.toLLong()); } + int64_t getInt64() { return static_cast(this->number.toLLong()); } + uint8_t getUint8() { return static_cast(this->number.toULLong()); } + uint16_t getUint16() { return static_cast(this->number.toULLong()); } + uint32_t getUint32() { return static_cast(this->number.toULLong()); } + uint64_t getUint64() { return static_cast(this->number.toULLong()); } float getFloat() { return this->number.toFloat(); } double getDouble() { return this->number.toDouble(); } diff --git a/source/include/lexer.h b/source/include/lexer.h index e9e679bc..9831a05c 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -141,7 +141,7 @@ namespace lexer inline void operator << (std::ostream& os, const TokenType& tt) { - os << (int) tt; + os << static_cast(tt); } // using TokenList = util::FastVector; diff --git a/source/include/sst.h b/source/include/sst.h index 74f4e55d..a618749d 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -5,7 +5,6 @@ #pragma once #include "defs.h" #include "sst_expr.h" -#include "stcommon.h" #include "mpreal/mpreal.h" diff --git a/source/include/sst_expr.h b/source/include/sst_expr.h index 130c1b4a..6b3d8170 100644 --- a/source/include/sst_expr.h +++ b/source/include/sst_expr.h @@ -5,6 +5,7 @@ #pragma once #include "defs.h" +#include "stcommon.h" namespace cgn { diff --git a/source/include/utils.h b/source/include/utils.h index 62b36c8c..935443c7 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -70,6 +70,17 @@ namespace util + template + std::vector vectorOf(const T& x) + { + return std::vector({ x }); + } + + template + std::vector vectorOf(const T& x, const Args&... xs) + { + return x + vectorOf(xs...); + } template ::type> diff --git a/source/main.cpp b/source/main.cpp index 60d1c04f..827cc60d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -20,8 +20,8 @@ struct timer { timer() : out(nullptr) { start = std::chrono::high_resolution_clock::now(); } explicit timer(double* t) : out(t) { start = std::chrono::high_resolution_clock::now(); } - ~timer() { if(out) *out = (double) (std::chrono::high_resolution_clock::now() - start).count() / 1000.0 / 1000.0; } - double stop() { return (double) (std::chrono::high_resolution_clock::now() - start).count() / 1000.0 / 1000.0; } + ~timer() { if(out) *out = static_cast((std::chrono::high_resolution_clock::now() - start).count()) / 1000000.0; } + double stop() { return static_cast((std::chrono::high_resolution_clock::now() - start).count()) / 1000000.0; } double* out = 0; std::chrono::time_point start; @@ -100,13 +100,16 @@ static void compile(std::string in, std::string out) if(frontend::getPrintProfileStats()) { - auto compile_ms = (double) (std::chrono::high_resolution_clock::now() - start_time).count() / 1000.0 / 1000.0; + auto compile_ms = static_cast((std::chrono::high_resolution_clock::now() - start_time).count()) / 1000.0 / 1000.0; debuglogln("cleared (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + debuglogln("compile (%.1f ms)\t[lex: %.1f, parse: %.1f, typechk: %.1f, codegen: %.1f]", compile_ms, lexer_ms, parser_ms, typecheck_ms, codegen_ms); - debuglogln("%d lines, %.2f loc/s, %d fir values\n", state.totalLinesOfCode, (double) state.totalLinesOfCode / (compile_ms / 1000.0), + + debuglogln("%d lines, %.2f loc/s, %d fir values\n", state.totalLinesOfCode, + static_cast(state.totalLinesOfCode) / (compile_ms / 1000.0), fir::Value::getCurrentValueId()); } @@ -137,7 +140,7 @@ static void compile(std::string in, std::string out) capsneeded |= BackendCaps::EmitProgram; } - if(backend->hasCapability((BackendCaps::Capabilities) capsneeded)) + if(backend->hasCapability(static_cast(capsneeded))) { backend->performCompilation(); backend->optimiseProgram(); @@ -146,7 +149,7 @@ static void compile(std::string in, std::string out) else { error("selected backend '%s' does not have some required capabilities (missing %s)\n", backend->str(), - capabilitiesToString((BackendCaps::Capabilities) capsneeded)); + capabilitiesToString(static_cast(capsneeded))); } } } diff --git a/source/misc/allocator.cpp b/source/misc/allocator.cpp index 70637aba..ac7dba16 100644 --- a/source/misc/allocator.cpp +++ b/source/misc/allocator.cpp @@ -33,7 +33,7 @@ namespace mem return ret; #else auto ret = mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if(ret == nullptr || (uintptr_t) ret == (uintptr_t) -1) + if(ret == nullptr || reinterpret_cast(ret) == static_cast(-1)) _error_and_exit("failed to allocate %d bytes of memory\n", bytes); return ret; diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 3d1d5a1b..1071af0b 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -53,7 +53,7 @@ void PolyArgMapping_t::add(const std::string& name, pts::Type* t) SingleArg arg; arg.name = name; arg.type = t; - arg.index = (size_t) -1; + arg.index = static_cast(-1); this->maps.push_back(arg); } diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 2208a2dc..22b73774 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -9,6 +9,7 @@ #include "frontend.h" #include "platform.h" + #if OS_WINDOWS #define WIN32_LEAN_AND_MEAN 1 @@ -198,8 +199,7 @@ namespace platform // explanation: if we have EXTRA_MMAP_FLAGS, then we're getting 2MB pages -- in which case we should probably only do it // if we have at least 4mb worth of file. // if not, then just 2 * pagesize. - #define MINIMUM_MMAP_THRESHOLD ((size_t) (EXTRA_MMAP_FLAGS ? (2 * 2 * 1024 * 1024) : 2 * getpagesize())) - #define _ + #define MINIMUM_MMAP_THRESHOLD (static_cast(EXTRA_MMAP_FLAGS ? (2 * 2 * 1024 * 1024) : 2 * getpagesize())) char* contents = 0; @@ -213,8 +213,8 @@ namespace platform if(fileLength >= MINIMUM_MMAP_THRESHOLD) { // ok, do an mmap - contents = (char*) mmap(0, fileLength, PROT_READ, MAP_PRIVATE | EXTRA_MMAP_FLAGS, fd, 0); - if(contents == MAP_FAILED) + contents = static_cast(mmap(0, fileLength, PROT_READ, MAP_PRIVATE | EXTRA_MMAP_FLAGS, fd, 0)); + if(contents == reinterpret_cast(-1)) { perror("there was an error reading the file"); exit(-1); @@ -366,6 +366,12 @@ namespace platform #endif } +#ifdef _MSC_VER +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + size_t getTerminalWidth() { #if OS_WINDOWS @@ -406,13 +412,13 @@ namespace platform #endif } -} - - - - +#ifdef _MSC_VER +#else + #pragma GCC diagnostic pop +#endif +} diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index e85e2f17..e5f5dbef 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -175,19 +175,6 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co cls->setMembers(tys); - - - /* - TODO: - - the check for method overriding here needs to check for co/contra variance, which we currently don't support. - we have virtual dispatch, so this is necessary. return types need to be covariant (ie. subclass method can only - return subclasses of the original return type), and parameters need to be contravariant (ie. the subclass method - must accept the superclasses of the original parameter types) - - currently i think we error, and we probably don't check for the return type at all? - */ - { //* check for what would be called 'method hiding' in c++, and also valid overrides. // TODO: make an error note about co/contra-variance for param/return types. right now it just complains and it's vague af. diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 1df3b6e7..4d312d12 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -283,7 +283,8 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(!args[0]->type->isCharType() && !args[0]->type->isStringType() && !args[0]->type->isCharSliceType()) { error(fc, "invalid argument type '%s' to builtin string method 'append'; expected one of '%s', '%s', or '%s'", - args[0]->type, fir::Type::getInt8(), (fir::Type*) fir::Type::getCharSlice(false), (fir::Type*) fir::Type::getString()); + args[0]->type, fir::Type::getInt8(), dcast(fir::Type, fir::Type::getCharSlice(false)), + dcast(fir::Type, fir::Type::getString())); } } @@ -363,7 +364,8 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d && args[0]->type != type) { error(fc, "invalid argument type '%s' to builtin array method 'append'; expected one of '%s', '%s', or '%s'", - args[0]->type, type, type->getArrayElementType(), (fir::Type*) fir::ArraySliceType::get(type->getArrayElementType(), false)); + args[0]->type, type, type->getArrayElementType(), + dcast(fir::Type, fir::ArraySliceType::get(type->getArrayElementType(), false))); } } diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 6066ec84..d6b98b04 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -260,7 +260,7 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) if(!this->isFunctionBody && !this->doNotPushNewScope) fs->pushAnonymousTree(); - defer((!this->isFunctionBody && !this->doNotPushNewScope) ? fs->popTree() : (sst::StateTree*) nullptr); + defer((!this->isFunctionBody && !this->doNotPushNewScope) ? fs->popTree() : static_cast(nullptr)); auto ret = util::pool(this->loc); diff --git a/source/typecheck/polymorph/solver.cpp b/source/typecheck/polymorph/solver.cpp index f7c9149d..d15a867b 100644 --- a/source/typecheck/polymorph/solver.cpp +++ b/source/typecheck/polymorph/solver.cpp @@ -42,7 +42,7 @@ namespace sst else { // limit decomposition of the given types by the number of transforms on the target type. - auto [ tt, ttrfs ] = internal::decomposeIntoTransforms(tgt, (size_t) -1); + auto [ tt, ttrfs ] = internal::decomposeIntoTransforms(tgt, static_cast(-1)); auto [ gt, gtrfs ] = internal::decomposeIntoTransforms(gvn, ttrfs.size()); // if(ttrfs != gtrfs) diff --git a/source/typecheck/polymorph/transforms.cpp b/source/typecheck/polymorph/transforms.cpp index 9ff735ef..4a3649a7 100644 --- a/source/typecheck/polymorph/transforms.cpp +++ b/source/typecheck/polymorph/transforms.cpp @@ -160,11 +160,11 @@ namespace poly case TrfType::None: break; case TrfType::Slice: - base = fir::ArraySliceType::get(base, (bool) it->data); + base = fir::ArraySliceType::get(base, static_cast(it->data)); break; case TrfType::Pointer: base = base->getPointerTo(); - if((bool) it->data) base = base->getMutablePointerVersion(); + if(static_cast(it->data)) base = base->getMutablePointerVersion(); break; case TrfType::FixedArray: base = fir::ArrayType::get(base, it->data); @@ -176,7 +176,7 @@ namespace poly base = fir::ArraySliceType::getVariadic(base); break; default: - error("unsupported transformation '%d'", (int) it->type); + error("unsupported transformation '%d'", static_cast(it->type)); } } return base; diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 894a03fa..70c2ff29 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -320,7 +320,7 @@ namespace resolver else if(auto t2 = arguments[1].value->type; fir::getCastDistance(t2, fir::Type::getNativeWord()) < 0) { error(arguments[0].loc, "second argument to two-arg string initialiser (length) must be '%s', found '%s' instead", - (fir::Type*) fir::Type::getNativeWord(), t2); + dcast(fir::Type, fir::Type::getNativeWord()), t2); } else { diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index dd32d7b9..b461fe91 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -202,7 +202,7 @@ void checkTraitConformity(sst::TypecheckState* fs, sst::TypeDefn* defn) for(const auto& m : missings) { err->append(SimpleError::make(MsgType::Note, std::get<0>(m), "missing implementation for method '%s': %s:", - std::get<1>(m), (fir::Type*) std::get<2>(m))); + std::get<1>(m), dcast(fir::Type, std::get<2>(m)))); } err->append( From 5390687ab11220ddd2de957367603abb15d71779 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 12 Oct 2019 23:55:47 +0800 Subject: [PATCH 002/129] fix `operator+` for `vector` in `util.h` to respect ordering! --- source/fir/IRBuilder.cpp | 12 +++++++----- source/include/utils.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index e0648b94..51b71808 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -424,7 +424,8 @@ namespace fir { iceAssert(v->getType()->isFloatingPointType() && targetType->isFloatingPointType() && "not floating point type"); Instruction* instr = make_instr(OpKind::Floating_Truncate, false, targetType, - util::vectorOf(v, ConstantValue::getZeroValue(targetType))); + util::vectorOf(v, ConstantValue::getZeroValue(targetType)) + ); return this->addInstruction(instr, vname); } @@ -1075,6 +1076,7 @@ namespace fir return this->addInstruction(instr, vname); } + Value* IRBuilder::CallVirtualMethod(ClassType* cls, FunctionType* ft, size_t index, const std::vector& args, const std::string& vname) { // args[0] must be the self, for obvious reasons. @@ -1088,11 +1090,11 @@ namespace fir util::vectorOf( ConstantValue::getZeroValue(cls), ConstantInt::getNative(index), - ConstantValue::getZeroValue(ft)) + args - ); - + ConstantValue::getZeroValue(ft) + ) + args + ); - // (Value*) ConstantValue::getZeroValue(cls) + ((Value*) ConstantInt::getNative(index) + ((Value*) ConstantValue::getZeroValue(ft) + args))); + // return this->addInstruction(instr, vname); } diff --git a/source/include/utils.h b/source/include/utils.h index 935443c7..6c7f78df 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -30,8 +30,8 @@ template std::vector operator + (const std::vector& a, const std::vector& b) { auto ret = a; + ret.insert(ret.end(), b.begin(), b.end()); - ret.insert(ret.begin(), b.begin(), b.end()); return ret; } From 5364ae7fd66b946efb9014e9a8453310757fa35d Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 12 Oct 2019 23:58:53 +0800 Subject: [PATCH 003/129] make attributes less ugly and slightly more extendable. --- build/supertiny.flx | 44 ++++++++++- build/ultratiny.flx | 118 +++------------------------- source/codegen/alloc.cpp | 2 +- source/codegen/function.cpp | 4 +- source/frontend/lexer.cpp | 6 ++ source/frontend/parser/expr.cpp | 68 +++++++++++----- source/frontend/parser/toplevel.cpp | 6 +- source/frontend/parser/type.cpp | 4 +- source/include/ast.h | 7 +- source/include/lexer.h | 1 + source/include/sst.h | 3 - source/include/sst_expr.h | 2 + source/include/stcommon.h | 46 +++++++++++ source/typecheck/alloc.cpp | 8 +- source/typecheck/function.cpp | 5 +- source/typecheck/unions.cpp | 12 +-- source/typecheck/variable.cpp | 1 + 17 files changed, 183 insertions(+), 154 deletions(-) diff --git a/build/supertiny.flx b/build/supertiny.flx index 1fc760bf..e256c9d6 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -106,7 +106,48 @@ import std::map */ +/* +class X +{ + init(c: int) + { + this.data = c + } + + var data: int +} + +class Y : X +{ + init(c: int) : super(c: c + 30) { } +} + +class Z : Y +{ + init(c: int) : super(c: c - 90) { } +} +class A +{ + init() { } + virtual fn foo(a: &Y) -> &Y + { + std::io::println("A::foo()") + return alloc Y(c: 471) + } +} + +class B : A +{ + init() : super() { } + + override fn foo(a: &X) -> &Z + { + std::io::println("B::foo()") + return alloc Z(c: 748) + } +} +*/ /* ! important stuff to fix ! @@ -114,9 +155,6 @@ import std::map 2. splatting tuples into a generic function *will not work* 3. pass the overall location to the polymorph solver. - 4. how does named-argument calling interact with varaidic arguments/functions? also, how do - optional arguments fit into that picure???? - 5. for optional arguments on virtual functions, we need to reach a decision --- do we ban it entirely, or follow what c++ does, which is to use the default values specified on the static type of the pointer? note: we are definitely *not* going to handle it at runtime. diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 008bca58..c58c1ab0 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -2,127 +2,34 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. -// export ultratiny -// import libc as _ -// import std::math - -// trait Drop { fn deinit() } -// trait Copy { fn copy(other: &self) } -// trait Move { fn move(other: &self) } - -// struct Foo : Drop, Copy, Move -// { -// fn deinit() -// { -// printf("lmao\n") -// } - -// fn copy(other: &self) -// { -// } - -// fn move(other: &mut self) -// { -// } -// } - -// @entry fn main() -// { -// printf("hola amigo\n") -// } - -import std::io -import libc as _ - -class X -{ - init(c: int) - { - this.data = c - } - - var data: int -} - -class Y : X -{ - init(c: int) : super(c: c + 30) { } -} +export ultratiny -class Z : Y -{ - init(c: int) : super(c: c - 90) { } -} - -// issue #2: class inheritance is source-order-dependent (ie. the definition of the base class must -// appear before the definition of any children) +import libc as _ +import std::math -// classes are a Bad Idea (tm) ): +@compiler_support trait Drop { fn deinit() } +@compiler_support trait Copy { fn copy(other: &self) } +@compiler_support trait Move { fn move(other: &mut self) } -class P +struct Foo : Drop, Copy, Move { - var z: int - - init(x: int = 3, y: int = 7) + fn deinit() { - this.z = x * y + printf("lmao\n") } - virtual fn foo(x: &Y) => printf("P::foo(%d)\n", x.data) -} - -class Q : P -{ - init() : super(x: 5, y: 8) { } - - // override fn foo(x: int) => printf("Q::foo(%d)\n", x) -} - -class R : Q -{ - init() : super() { } - - override fn foo(x: &X) => printf("R::foo(%d)\n", x.data) -} - - - -class A -{ - init() { } - virtual fn foo(a: &Y) -> &Y + fn copy(other: &self) { - std::io::println("A::foo()") - return alloc Y(c: 471) } -} - -class B : A -{ - init() : super() { } - override fn foo(a: &X) -> &Z + fn move(other: &mut self) { - std::io::println("B::foo()") - return alloc Z(c: 748) } } - @entry fn main() { - fn foo(x: int = 3, y: int = 7) -> int => x * y - - // let a = B() - // let q = a.foo(Y(c: 1)).data - - // let y: &X = alloc Y(c: 41) - // std::io::println("y = %", y.data) - - // let q = Tmp(k: 3, m: 7).meth() - - let q = B().foo(alloc Y(c: 1)).data - printf("q = %d\n", q) + printf("hola amigo\n") } @@ -144,5 +51,6 @@ class B : A + diff --git a/source/codegen/alloc.cpp b/source/codegen/alloc.cpp index 8a34ef16..2f261934 100644 --- a/source/codegen/alloc.cpp +++ b/source/codegen/alloc.cpp @@ -189,7 +189,7 @@ CGResult sst::AllocOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->counts.size() > 1) error(this, "multi-dimensional arrays are not supported yet."); - return CGResult(performAllocation(cs, this, this->elmType, this->counts, this->isRaw)); + return CGResult(performAllocation(cs, this, this->elmType, this->counts, this->attrs.has(attr::RAW))); } diff --git a/source/codegen/function.cpp b/source/codegen/function.cpp index 6633d841..ee96776f 100644 --- a/source/codegen/function.cpp +++ b/source/codegen/function.cpp @@ -24,7 +24,7 @@ CGResult sst::FunctionDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto ft = fir::FunctionType::get(ptypes, this->returnType); auto ident = this->id; - if(this->isEntry || this->noMangle) + if(this->attrs.hasAny(attr::FN_ENTRYPOINT, attr::NO_MANGLE)) ident = Identifier(this->id.name, IdKind::Name); auto fn = cs->module->getOrCreateFunction(ident, ft, @@ -82,7 +82,7 @@ CGResult sst::FunctionDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->parentTypeForMethod) cs->leaveMethodBody(); - if(this->isEntry) + if(this->attrs.has(attr::FN_ENTRYPOINT)) { if(cs->entryFunction.first != 0) { diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index affedd70..2cf3d6b2 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -419,6 +419,12 @@ namespace lexer tok.text = "@platform"; read = 9; } + else if(hasPrefix(stream, "@compiler_support")) + { + tok.type = TokenType::Attr_CompilerSupport; + tok.text = "@compiler_support"; + read = 17; + } // directives else if(hasPrefix(stream, "#if")) diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 7618cbad..42826524 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -98,65 +98,87 @@ namespace parser auto tok = st.front(); if(tok != TT::EndOfFile) { + Stmt* ret = 0; + + bool compiler_support = false; + if(tok.type == TT::Attr_CompilerSupport) + compiler_support = true, st.pop(), tok = st.front(); + // handle the things that are OK to appear anywhere first: switch(tok.type) { case TT::Var: case TT::Val: - return parseVariable(st); + ret = parseVariable(st); + break; case TT::Func: - return parseFunction(st); + ret = parseFunction(st); + break; case TT::ForeignFunc: - return parseForeignFunction(st); + ret = parseForeignFunction(st); + break; case TT::Public: case TT::Private: case TT::Internal: - return parseStmtWithAccessSpec(st); + ret = parseStmtWithAccessSpec(st); + break; case TT::Directive_If: - return parseIfStmt(st); + ret = parseIfStmt(st); + break; case TT::Directive_Run: - return parseRunDirective(st); + ret = parseRunDirective(st); + break; case TT::Attr_Raw: st.eat(); if(st.front() != TT::Union) expectedAfter(st.loc(), "'union'", "'@raw' while parsing statement", st.front().str()); - return parseUnion(st, /* isRaw: */ true, /* nameless: */ false); + ret = parseUnion(st, /* isRaw: */ true, /* nameless: */ false); + break; case TT::Union: - return parseUnion(st, /* isRaw: */ false, /* nameless: */ false); + ret = parseUnion(st, /* isRaw: */ false, /* nameless: */ false); + break; case TT::Struct: - return parseStruct(st, /* nameless: */ false); + ret = parseStruct(st, /* nameless: */ false); + break; case TT::Class: - return parseClass(st); + ret = parseClass(st); + break; case TT::Enum: - return parseEnum(st); + ret = parseEnum(st); + break; case TT::Trait: - return parseTrait(st); + ret = parseTrait(st); + break; case TT::Static: - return parseStaticDecl(st); + ret = parseStaticDecl(st); + break; case TT::Operator: - return parseOperatorOverload(st); + ret = parseOperatorOverload(st); + break; case TT::Using: - return parseUsingStmt(st); + ret = parseUsingStmt(st); + break; case TT::Mutable: case TT::Virtual: case TT::Override: - return checkMethodModifiers(false, false, false); + ret = checkMethodModifiers(false, false, false); + break; case TT::Extension: case TT::TypeAlias: @@ -182,12 +204,20 @@ namespace parser break; } + if(ret) + { + if(compiler_support) + ret->attrs.set(attr::COMPILER_SUPPORT); + + return ret; + } + // if we got here, then it wasn't any of those things. // we store it first, so we can give better error messages (after knowing what it is) // in the event that it wasn't allowed at top-level. - Stmt* ret = 0; + switch(tok.type) { case TT::If: @@ -1014,7 +1044,9 @@ namespace parser if(st.front() == TT::Mutable) ret->isMutable = true, st.pop(); - ret->isRaw = raw; + if(raw) + ret->attrs.set(attr::RAW); + ret->allocTy = parseType(st); if(st.front() == TT::LParen) diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index 5ee271e1..d4a4d44a 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -184,10 +184,10 @@ namespace parser warn(st, "attribute '@nomangle' is redundant on 'ffi' functions"); else if(auto fd = dcast(FuncDefn, stmt)) - fd->noMangle = true; + fd->attrs.set(attr::NO_MANGLE); else if(auto vd = dcast(VarDefn, stmt)) - vd->noMangle = true; + vd->attrs.set(attr::NO_MANGLE); root->statements.push_back(stmt); @@ -201,7 +201,7 @@ namespace parser st.pop(); auto stmt = parseStmt(st); if(auto fd = dcast(FuncDefn, stmt)) - fd->isEntry = true; + fd->attrs.set(attr::FN_ENTRYPOINT); else error(st, "'@entry' attribute is only applicable to function definitions"); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index dfbcb049..d4f0c1e0 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -421,7 +421,9 @@ namespace parser iceAssert(st.front() == TT::RBrace); st.eat(); - defn->israw = israw; + if(israw) + defn->attrs.set(attr::RAW); + return defn; } diff --git a/source/include/ast.h b/source/include/ast.h index 14dc3efc..5237e4e4 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -35,6 +35,8 @@ namespace ast Stmt(const Location& l) : Locatable(l, "statement") { } virtual ~Stmt(); virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) = 0; + + AttributeSet attrs; }; struct Expr : Stmt @@ -157,9 +159,6 @@ namespace ast Block* body = 0; - bool isEntry = false; - bool noMangle = false; - bool isMutating = false; bool isVirtual = false; @@ -492,7 +491,6 @@ namespace ast virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer, const TypeParamMap_t& gmaps) override; virtual TCResult generateDeclaration(sst::TypecheckState* fs, fir::Type* infer, const TypeParamMap_t& gmaps) override; - bool israw = false; util::hash_map> cases; std::vector> transparentFields; @@ -578,7 +576,6 @@ namespace ast Block* initBody = 0; - bool isRaw = false; bool isMutable = false; }; diff --git a/source/include/lexer.h b/source/include/lexer.h index 9831a05c..90f88a81 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -123,6 +123,7 @@ namespace lexer Attr_NoMangle, Attr_Operator, Attr_Platform, + Attr_CompilerSupport, Directive_Run, Directive_If, diff --git a/source/include/sst.h b/source/include/sst.h index a618749d..22560ac1 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -218,7 +218,6 @@ namespace sst VarDefn* initBlockIdx = 0; Block* initBlock = 0; - bool isRaw = false; bool isMutable = false; }; @@ -623,8 +622,6 @@ namespace sst fir::Type* returnType = 0; fir::Type* parentTypeForMethod = 0; - bool isEntry = false; - bool noMangle = false; bool isVarArg = false; virtual std::string getKind() override { return "function"; } diff --git a/source/include/sst_expr.h b/source/include/sst_expr.h index 6b3d8170..435c0d4d 100644 --- a/source/include/sst_expr.h +++ b/source/include/sst_expr.h @@ -25,6 +25,8 @@ namespace sst size_t cachedCSId = 0; bool didCodegen = false; CGResult cachedResult = CGResult(0); + + AttributeSet attrs; }; struct Expr : Stmt diff --git a/source/include/stcommon.h b/source/include/stcommon.h index a475d757..e3754af5 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -15,6 +15,52 @@ namespace ast struct Expr; } +namespace attr +{ + constexpr uint32_t FN_ENTRYPOINT = 0x1; + constexpr uint32_t NO_MANGLE = 0x2; + constexpr uint32_t RAW = 0x4; + constexpr uint32_t COMPILER_SUPPORT = 0x8; +} + +struct AttributeSet +{ + struct UserAttrib + { + std::string name; + std::vector args; + }; + + std::vector userAttribs; + + template + bool has(T x) const { return (this->flags & x); } + + template + bool hasAny(T x) const { return (this->flags & x); } + + template + bool hasAny(T x, Args... xs) const { return (this->flags & x) || hasAny(xs...); } + + template + bool hasAll(T x) const { return (this->flags & x); } + + template + bool hasAll(T x, Args... xs) const { return (this->flags & x) && hasAll(xs...); } + + template + void set(T x) { this->flags |= x; } + + template + void set(T x, Args... xs) { this->flags |= x; set(xs...); } + + + + private: + uint32_t flags; +}; + + struct DecompMapping { Location loc; diff --git a/source/typecheck/alloc.cpp b/source/typecheck/alloc.cpp index c4153e37..b98721a8 100644 --- a/source/typecheck/alloc.cpp +++ b/source/typecheck/alloc.cpp @@ -20,7 +20,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) fir::Type* elm = fs->convertParserTypeToFIR(this->allocTy); iceAssert(elm); - if(this->isRaw && this->counts.size() > 1) + if(this->attrs.has(attr::RAW) && this->counts.size() > 1) error(this, "only one length dimension is supported for raw memory allocation (have %d)", this->counts.size()); std::vector counts = util::map(this->counts, [fs](ast::Expr* e) -> auto { @@ -36,7 +36,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) error(this, "cannot provide arguments to non-struct type '%s'", elm); - fir::Type* resType = (this->isRaw || counts.empty() ? + fir::Type* resType = (this->attrs.has(attr::RAW) || counts.empty() ? (this->isMutable ? elm->getMutablePointerTo() : elm->getPointerTo()) : fir::DynamicArrayType::get(elm)); auto ret = util::pool(this->loc, resType); @@ -68,7 +68,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(this->initBody) { - iceAssert(!this->isRaw && this->counts.size() > 0); + iceAssert(!this->attrs.has(attr::RAW) && this->counts.size() > 0); // ok, make a fake vardefn and insert it first. auto fake = util::pool(this->initBody->loc); @@ -94,7 +94,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) ret->elmType = elm; ret->counts = counts; - ret->isRaw = this->isRaw; + ret->attrs = this->attrs; ret->isMutable = this->isMutable; return TCResult(ret); diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index d6b98b04..5ea340ea 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -57,6 +57,7 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->parentTypeForMethod = infer; + defn->attrs = this->attrs; defn->bareName = this->name; defn->id = Identifier(this->name, IdKind::Function); defn->id.scope = this->realScope; @@ -66,10 +67,6 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->returnType = retty; defn->visibility = this->visibility; - defn->isEntry = this->isEntry; - defn->noMangle = this->noMangle; - - defn->global = !fs->isInFunctionBody(); defn->isVirtual = this->isVirtual; diff --git a/source/typecheck/unions.cpp b/source/typecheck/unions.cpp index 927de574..1cb1dbaa 100644 --- a/source/typecheck/unions.cpp +++ b/source/typecheck/unions.cpp @@ -27,9 +27,11 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* auto defnname = util::typeParamMapToString(this->name, gmaps); + bool israw = this->attrs.has(attr::RAW); + sst::TypeDefn* defn = 0; - if(this->israw) defn = util::pool(this->loc); - else defn = util::pool(this->loc); + if(israw) defn = util::pool(this->loc); + else defn = util::pool(this->loc); defn->bareName = this->name; @@ -38,8 +40,8 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->visibility = this->visibility; defn->original = this; - if(this->israw) defn->type = fir::RawUnionType::createWithoutBody(defn->id); - else defn->type = fir::UnionType::createWithoutBody(defn->id); + if(israw) defn->type = fir::RawUnionType::createWithoutBody(defn->id); + else defn->type = fir::UnionType::createWithoutBody(defn->id); fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); @@ -71,7 +73,7 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co sst::TypeDefn* ret = 0; - if(this->israw) + if(this->attrs.has(attr::RAW)) { auto defn = dcast(sst::RawUnionDefn, tcr.defn()); iceAssert(defn); diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 7c202440..75e50e3a 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -320,6 +320,7 @@ TCResult ast::VarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) iceAssert(defn); defn->bareName = this->name; + defn->attrs = this->attrs; defn->id = Identifier(this->name, IdKind::Name); defn->id.scope = fs->getCurrentScope(); From 353dac8a0e4f4c8f8785a457bfcd5310d5a41eb5 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 13 Oct 2019 14:02:49 +0800 Subject: [PATCH 004/129] some cleanup, add backtrace on hard crash --- source/backend/llvm/translator.cpp | 2 +- source/fir/IRBuilder.cpp | 2 - source/include/platform.h | 2 + source/main.cpp | 1 + source/platform/backtrace.cpp | 123 +++++++++++++++++++++++++++++ source/platform/platform.cpp | 20 +++++ 6 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 source/platform/backtrace.cpp diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index d8e1abd5..9c0255ec 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -2332,7 +2332,7 @@ namespace backend auto ut = inst->operands[0]->getType()->toUnionType(); auto vid = dcast(fir::ConstantInt, inst->operands[1])->getSignedValue(); - iceAssert(static_cast(vid < ut->getVariantCount())); + iceAssert(static_cast(vid) < ut->getVariantCount()); auto vt = ut->getVariant(vid)->getInteriorType(); auto lut = typeToLlvm(ut, module); diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 51b71808..4ade495a 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -1094,8 +1094,6 @@ namespace fir ) + args ); - // - return this->addInstruction(instr, vname); } diff --git a/source/include/platform.h b/source/include/platform.h index 8cf93f2b..30fb6355 100644 --- a/source/include/platform.h +++ b/source/include/platform.h @@ -87,6 +87,8 @@ namespace platform void pushEnvironmentVar(const std::string& name, const std::string& value); void popEnvironmentVar(const std::string& name); + void printStackTrace(); + void setupCrashHandlers(); namespace compiler { diff --git a/source/main.cpp b/source/main.cpp index 827cc60d..fe2fc5ed 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -158,6 +158,7 @@ static void compile(std::string in, std::string out) int main(int argc, char** argv) { + platform::setupCrashHandlers(); platform::setupTerminalIfNecessary(); platform::compiler::performSelfDlOpen(); diff --git a/source/platform/backtrace.cpp b/source/platform/backtrace.cpp new file mode 100644 index 00000000..1e28b99a --- /dev/null +++ b/source/platform/backtrace.cpp @@ -0,0 +1,123 @@ +// backtrace.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include + +#include "errors.h" +#include "platform.h" + +#if OS_DARWIN +#include + +// note: we declare these ourselves because there's some issue with execinfo.h on osx ): +extern "C" { + int backtrace(void**, int); + char** backtrace_symbols(void* const*, int); +} +#elif OS_UNIX +#include +#include +#endif + + +namespace platform +{ + constexpr size_t MAX_FRAMES = 128; + + struct piece_t + { + size_t num; + uintptr_t address; + ssize_t offset; + + std::string modName; + std::string mangledName; + std::string demangledName; + }; + + void printStackTrace() + { + #if OS_WINDOWS + + + #else + + void* arr[MAX_FRAMES] { }; + size_t num = backtrace(arr, MAX_FRAMES); + char** strs = backtrace_symbols(arr, num); + + std::vector pieces; + + for(size_t i = 5; i < num; i++) + { + // platform-specific output! + if constexpr (OS_DARWIN) + { + piece_t piece; + piece.num = i; + + char modname[1024] { }; + char funcname[1024] { }; + + sscanf(strs[i], "%*s %s %zx %s %*s %zd", &modname[0], &piece.address, &funcname[0], &piece.offset); + + piece.mangledName = funcname; + piece.modName = modname; + + if(piece.mangledName.find("_Z") == 0) + { + int status = -1; + char* demangledName = abi::__cxa_demangle(piece.mangledName.c_str(), nullptr, nullptr, &status); + if(status == 0) + { + std::string deman = demangledName; + free(demangledName); + + // do replacements. + std::map replacements = { + { "std::__1::basic_string, std::__1::allocator >", "std::string" } + }; + + for(const auto& [ from, to ] : replacements) + { + size_t it = -1; + while((it = deman.find(from)) != std::string::npos) + deman.replace(it, from.size(), to); + } + + piece.demangledName = deman; + } + else + { + piece.demangledName = "??"; + } + + // skip some. + if(piece.demangledName == "sst::Stmt::codegen(cgn::CodegenState*, fir::Type*)") + continue; + } + else + { + piece.demangledName = piece.mangledName; + } + + debuglogln(" %2d: %12p | %s + %#x", piece.num, piece.address, piece.demangledName, piece.offset); + } + else + { + } + } + #endif + } +} + + + + + + + + + + diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 22b73774..7b92b1f6 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -413,6 +413,26 @@ namespace platform #endif } + void setupCrashHandlers() + { + #if OS_WINDOWS + + #else + signal(SIGSEGV, [](int) -> void { + constexpr const char* msg = COLOUR_RED_BOLD "\n\ncompiler crash! " COLOUR_RESET "(segmentation fault)\n" + COLOUR_BLUE_BOLD "stacktrace:\n" COLOUR_RESET; + + write(1, msg, strlen(msg)); + + // note: this does not care about being re-entrant in signal handlers. + // fuck that noise. + printStackTrace(); + + abort(); + }); + #endif + } + #ifdef _MSC_VER #else #pragma GCC diagnostic pop From 571fd0c2f0e3495213a27e385a17fa54f373bde6 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 13 Oct 2019 20:43:38 +0800 Subject: [PATCH 005/129] fix the build on linux --- source/platform/platform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 7b92b1f6..1bd405dd 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -23,6 +23,7 @@ #else #include #include + #include #include #include From 744095cd0fe08e3513e3e28d6e89094ef73d806f Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 13 Oct 2019 21:07:20 +0800 Subject: [PATCH 006/129] fix semaphore2 build --- .semaphore/semaphore.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 756bed80..136c0a23 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -22,5 +22,5 @@ blocks: - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-7 llvm-7-dev libllvm7 libmpfr-dev libmpfr6 - CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-7 make -j2 build - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend interp build/tester.flx + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile build/tester.flx && ./tester From 06a397a2802ce700081b0684b3576f061b8eb0e4 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 13 Oct 2019 23:34:23 +0800 Subject: [PATCH 007/129] preliminary support for @compiler_support, and the Drop trait appears to work correctly. --- build/ultratiny.flx | 43 +++++++- meson.build | 2 +- source/codegen/misc.cpp | 51 ++++++++++ source/codegen/raii.cpp | 97 +++++++++++++++--- source/codegen/toplevel.cpp | 1 + source/frontend/errors.cpp | 2 +- source/frontend/parser/expr.cpp | 145 +++++++++++++-------------- source/frontend/parser/misc.cpp | 64 ++++++++++++ source/frontend/parser/operators.cpp | 1 + source/include/ast.h | 2 +- source/include/codegen.h | 14 ++- source/include/lexer.h | 4 + source/include/parser_internal.h | 1 + source/include/sst_expr.h | 2 +- source/include/stcommon.h | 69 ++++++++----- source/include/typecheck.h | 1 + source/include/utils.h | 24 +++++ source/typecheck/classes.cpp | 1 + source/typecheck/function.cpp | 1 + source/typecheck/structs.cpp | 1 + source/typecheck/toplevel.cpp | 24 ++++- source/typecheck/traits.cpp | 2 + source/typecheck/unions.cpp | 1 + 23 files changed, 422 insertions(+), 131 deletions(-) diff --git a/build/ultratiny.flx b/build/ultratiny.flx index c58c1ab0..3bd4dcbb 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -7,36 +7,69 @@ export ultratiny import libc as _ import std::math -@compiler_support trait Drop { fn deinit() } -@compiler_support trait Copy { fn copy(other: &self) } -@compiler_support trait Move { fn move(other: &mut self) } +@compiler_support["raii_trait::drop"] trait Drop { fn deinit() } +@compiler_support["raii_trait::copy"] trait Copy { fn copy(other: &self) } +@compiler_support["raii_trait::move"] trait Move { fn move(other: &mut self) } struct Foo : Drop, Copy, Move { + data: int + fn deinit() { - printf("lmao\n") + printf("is kill\n") } fn copy(other: &self) { + printf("am copy\n") } fn move(other: &mut self) { + printf("be move\n") + } +} + +class Bar +{ + var data: int + init(x: int) + { + this.data = x } } +fn stuff() -> Foo +{ + return Foo(data: 33) +} + +fn stuff2() -> Bar +{ + return Bar(x: 57) +} + @entry fn main() { - printf("hola amigo\n") + let q = stuff() + // let p = stuff2() + printf("q = %d\n", q) } +/* + raii traits checklist: + // 1. call the appropriate methods on structs. + (kind of: still need Copy and Move) + // 4. implement @compiler_support + 2. generalise the raii mechanism to by searching for traits instead of hardcoding for classes + 3. remove the copy/move/destruct stuff from FIR? +*/ diff --git a/meson.build b/meson.build index dfb2bbab..9b4b62bb 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ the_compiler = meson.get_compiler('c') if the_compiler.get_id() == 'msvc' add_project_arguments('/utf-8', language: ['c', 'cpp']) - add_project_arguments('/std:c++17', language: 'cpp') + add_project_arguments('/std:c++latest', language: 'cpp') add_project_arguments('/permissive-', language: 'cpp') add_project_arguments('-D_STDC_LIMIT_MACROS', language: 'cpp') add_project_arguments('-D_SCL_SECURE_NO_WARNINGS', language: ['c', 'cpp']) diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index e3d8bd15..7cb0f41d 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -43,6 +43,57 @@ CGResult sst::Stmt::codegen(cgn::CodegenState* cs, fir::Type* inferred) return (this->cachedResult = this->_codegen(cs, inferred)); } } + +// TODO: move this impl somewhere else? +sst::FunctionDefn* cgn::CodegenState::findMatchingMethodInType(sst::TypeDefn* td, sst::FunctionDecl* fn) +{ + if(auto str = dcast(sst::StructDefn, td); str) + { + // TODO: when (if) we figure out what's going on in typecheck/traits.cpp:129, possibly change this to match. + auto it = std::find_if(str->methods.begin(), str->methods.end(), [this, fn](sst::FunctionDefn* method) -> bool { + + //* i think this check should work, `areMethodsVirtuallyCompatible` basically checks the parameters but takes + //* co/contravariance into account and doesn't match the first (self) parameter. + return (fn->id.name == method->id.name && fir::ClassType::areMethodsVirtuallyCompatible( + fn->type->toFunctionType(), method->type->toFunctionType()) + ); + }); + + if(it != str->methods.end()) + return *it; + } + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index 38ce11ba..b9070102 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -10,7 +10,7 @@ namespace cgn { void CodegenState::addRAIIValue(fir::Value* val) { - if(!val->getType()->isClassType()) + if(!this->isRAIIType(val->getType())) error("val is not a class type! '%s'", val->getType()); auto list = &this->blockPointStack.back().raiiValues; @@ -26,7 +26,7 @@ namespace cgn void CodegenState::removeRAIIValue(fir::Value* val) { - if(!val->getType()->isClassType()) + if(!this->isRAIIType(val->getType())) error("val is not a class type! '%s'", val->getType()); auto list = &this->blockPointStack.back().raiiValues; @@ -47,10 +47,13 @@ namespace cgn void CodegenState::addRAIIOrRCValueIfNecessary(fir::Value* val, fir::Type* ty) { - if(fir::isRefCountedType(ty ? ty : val->getType())) + if(!ty) + ty = val->getType(); + + if(fir::isRefCountedType(ty)) this->addRefCountedValue(val); - if((ty ? ty : val->getType())->isClassType()) + if(this->isRAIIType(ty)) this->addRAIIValue(val); } @@ -73,19 +76,43 @@ namespace cgn void CodegenState::callDestructor(fir::Value* val) { - if(!val->getType()->isClassType()) - error("val is not a class type! '%s'", val->getType()); + if(!this->typeHasDestructor(val->getType())) + return; fir::Value* selfptr = getAddressOfOrMakeTemporaryLValue(this, val, /* mutable: */ true); - auto cls = val->getType()->toClassType(); - // call the user-defined one first, if any: - if(auto des = cls->getDestructor(); des) - this->irb.Call(des, selfptr); + if(val->getType()->isClassType()) + { + auto cls = val->getType()->toClassType(); + + // call the user-defined one first, if any: + if(auto des = cls->getDestructor(); des) + this->irb.Call(des, selfptr); + + // call the auto one. this will handle calling base class destructors for us! + this->irb.Call(cls->getInlineDestructor(), selfptr); + } + else + { + if(auto it = this->compilerSupportDefinitions.find("raii_trait::drop"); it != this->compilerSupportDefinitions.end()) + { + auto trt = dcast(sst::TraitDefn, it->second); + if(!trt) error("invalid use of @compiler_support[\"raii_trait::drop\"] on non-trait definition!"); + + iceAssert(trt->methods.size() == 1); + + auto str = dcast(sst::StructDefn, this->typeDefnMap[val->getType()]); + iceAssert(str); - // call the auto one. this will handle calling base class destructors for us! - this->irb.Call(cls->getInlineDestructor(), selfptr); + auto destructor = this->findMatchingMethodInType(str, trt->methods[0]); + if(destructor) + { + this->irb.Call(dcast(fir::Function, destructor->codegen(this).value), selfptr); + return; + } + } + } } @@ -102,8 +129,9 @@ namespace cgn auto clsty = from->getType()->toClassType(); + // TODO: this is a shitty error message. if(!target->islvalue()) - error("invalid constructor on non-lvalue"); + error(cs->loc(), "invalid operation on non-lvalue"); return clsty; } @@ -204,7 +232,6 @@ namespace cgn return; } - if(auto movecon = clsty->getMoveConstructor(); movecon) { auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); @@ -219,6 +246,48 @@ namespace cgn this->removeRAIIValue(from); } + + + + static bool findRAIITraitImpl(CodegenState* cs, fir::Type* ty, const std::string& name) + { + if(ty->isClassType()) + return true; + + if(!ty->isStructType()) + return false; + + auto str = ty->toStructType(); + auto def = dcast(sst::StructDefn, cs->typeDefnMap[str]); + iceAssert(def); + + return util::matchAny(def->traits, [name](auto trt) -> bool { + return trt->id.name == name; + }); + } + + // TODO: + //* after @compiler_support is implemented properly, these should probably use that mechanism to + //* find the name of the trait to search. + bool CodegenState::typeHasDestructor(fir::Type* ty) + { + return findRAIITraitImpl(this, ty, "Drop"); + } + + bool CodegenState::typeHasCopyConstructor(fir::Type* ty) + { + return findRAIITraitImpl(this, ty, "Copy"); + } + + bool CodegenState::typeHasMoveConstructor(fir::Type* ty) + { + return findRAIITraitImpl(this, ty, "Move"); + } + + bool CodegenState::isRAIIType(fir::Type* ty) + { + return this->typeHasDestructor(ty) || this->typeHasCopyConstructor(ty) || this->typeHasMoveConstructor(ty); + } } diff --git a/source/codegen/toplevel.cpp b/source/codegen/toplevel.cpp index 0db16f4a..9dbf7482 100644 --- a/source/codegen/toplevel.cpp +++ b/source/codegen/toplevel.cpp @@ -31,6 +31,7 @@ namespace cgn cs->module = mod; cs->typeDefnMap = dtr->typeDefnMap; + cs->compilerSupportDefinitions = dtr->compilerSupportDefinitions; { cs->pushLoc(dtr->topLevel); diff --git a/source/frontend/errors.cpp b/source/frontend/errors.cpp index 3c1cf363..7fbf641a 100644 --- a/source/frontend/errors.cpp +++ b/source/frontend/errors.cpp @@ -250,7 +250,7 @@ void SimpleError::post() { if(!this->msg.empty()) { - outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str(), !this->subs.empty()); + outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str(), true); strprinterrf("%s%s%s", this->wordsBeforeContext, this->wordsBeforeContext.size() > 0 ? "\n" : "", this->printContext ? getSingleContext(this->loc, this->type == MsgType::Note ? COLOUR_BLUE_BOLD : COLOUR_RED_BOLD) + "\n" : ""); } diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 42826524..b1597ba5 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -98,89 +98,89 @@ namespace parser auto tok = st.front(); if(tok != TT::EndOfFile) { - Stmt* ret = 0; + auto attrs = parseAttributes(st); + auto enforceAttrs = [&attrs](Stmt* ret, const AttribSet& allowed = AttribSet::of(attr::NONE)) -> Stmt* { + + using namespace attr; + + // there's probably a better way to do this, but bleugh + if((attrs.flags & RAW) && !(allowed.flags & RAW)) + error(ret, "unsupported attribute '@raw' on %s", ret->readableName); + + if((attrs.flags & NO_MANGLE) && !(allowed.flags & NO_MANGLE)) + error(ret, "unsupported attribute '@nomangle' on %s", ret->readableName); + + if((attrs.flags & FN_ENTRYPOINT) && !(allowed.flags & FN_ENTRYPOINT)) + error(ret, "unsupported attribute '@entry' on %s", ret->readableName); + + // here let's check the arguments and stuff for default attributes. + // note: due to poor API design on my part, if there is no attribute with that name then ::get() + // returns an empty UA, which has a blank name -- so we check that instead. - bool compiler_support = false; - if(tok.type == TT::Attr_CompilerSupport) - compiler_support = true, st.pop(), tok = st.front(); + if(auto ua = attrs.get("@compiler_support"); !ua.name.empty() && ua.args.size() != 1) + error(ret, "@compiler_support requires exactly one argument"); + + // actually that's it + + ret->attrs = attrs; + return ret; + }; // handle the things that are OK to appear anywhere first: + tok = st.front(); switch(tok.type) { - case TT::Var: + case TT::Var: [[fallthrough]]; case TT::Val: - ret = parseVariable(st); - break; + return enforceAttrs(parseVariable(st), AttribSet::of(attr::NO_MANGLE)); case TT::Func: - ret = parseFunction(st); - break; + return enforceAttrs(parseFunction(st), AttribSet::of(attr::NO_MANGLE | attr::FN_ENTRYPOINT)); case TT::ForeignFunc: - ret = parseForeignFunction(st); - break; + return enforceAttrs(parseForeignFunction(st)); - case TT::Public: - case TT::Private: + case TT::Public: [[fallthrough]]; + case TT::Private: [[fallthrough]]; case TT::Internal: - ret = parseStmtWithAccessSpec(st); - break; + return enforceAttrs(parseStmtWithAccessSpec(st)); case TT::Directive_If: - ret = parseIfStmt(st); - break; + return enforceAttrs(parseIfStmt(st)); case TT::Directive_Run: - ret = parseRunDirective(st); - break; - - case TT::Attr_Raw: - st.eat(); - if(st.front() != TT::Union) - expectedAfter(st.loc(), "'union'", "'@raw' while parsing statement", st.front().str()); - - ret = parseUnion(st, /* isRaw: */ true, /* nameless: */ false); - break; + return enforceAttrs(parseRunDirective(st)); case TT::Union: - ret = parseUnion(st, /* isRaw: */ false, /* nameless: */ false); - break; + return enforceAttrs(parseUnion(st, attrs.has(attr::RAW), /* nameless: */ false), AttribSet::of(attr::RAW)); case TT::Struct: - ret = parseStruct(st, /* nameless: */ false); - break; + return enforceAttrs(parseStruct(st, /* nameless: */ false)); case TT::Class: - ret = parseClass(st); - break; + return enforceAttrs(parseClass(st)); case TT::Enum: - ret = parseEnum(st); - break; + return enforceAttrs(parseEnum(st)); case TT::Trait: - ret = parseTrait(st); - break; + return enforceAttrs(parseTrait(st)); case TT::Static: - ret = parseStaticDecl(st); - break; + return enforceAttrs(parseStaticDecl(st)); case TT::Operator: - ret = parseOperatorOverload(st); - break; + return enforceAttrs(parseOperatorOverload(st)); case TT::Using: - ret = parseUsingStmt(st); - break; + return enforceAttrs(parseUsingStmt(st)); - case TT::Mutable: - case TT::Virtual: + case TT::Mutable: [[fallthrough]]; + case TT::Virtual: [[fallthrough]]; case TT::Override: - ret = checkMethodModifiers(false, false, false); - break; + return enforceAttrs(checkMethodModifiers(false, false, false), AttribSet::of(attr::NO_MANGLE)); - case TT::Extension: + case TT::Extension: [[fallthrough]]; case TT::TypeAlias: error(st, "notsup"); @@ -204,20 +204,13 @@ namespace parser break; } - if(ret) - { - if(compiler_support) - ret->attrs.set(attr::COMPILER_SUPPORT); - - return ret; - } - // if we got here, then it wasn't any of those things. // we store it first, so we can give better error messages (after knowing what it is) // in the event that it wasn't allowed at top-level. + Stmt* ret = 0; switch(tok.type) { case TT::If: @@ -231,7 +224,7 @@ namespace parser ret = parseReturn(st); break; - case TT::Do: + case TT::Do: [[fallthrough]]; case TT::While: ret = parseWhileLoop(st); break; @@ -413,20 +406,20 @@ namespace parser // unary ... (splat) // ^^ all have 950. - case TT::As: + case TT::As: [[fallthrough]]; case TT::Is: return 900; - case TT::DoublePlus: + case TT::DoublePlus: [[fallthrough]]; case TT::DoubleMinus: return 850; - case TT::Asterisk: - case TT::Divide: + case TT::Asterisk: [[fallthrough]]; + case TT::Divide: [[fallthrough]]; case TT::Percent: return 800; - case TT::Plus: + case TT::Plus: [[fallthrough]]; case TT::Minus: return 750; @@ -443,15 +436,15 @@ namespace parser case TT::Pipe: return 550; - case TT::LAngle: - case TT::RAngle: - case TT::LessThanEquals: - case TT::GreaterEquals: - case TT::EqualsTo: + case TT::LAngle: [[fallthrough]]; + case TT::RAngle: [[fallthrough]]; + case TT::LessThanEquals:[[fallthrough]]; + case TT::GreaterEquals: [[fallthrough]]; + case TT::EqualsTo: [[fallthrough]]; case TT::NotEquals: return 500; - case TT::Ellipsis: + case TT::Ellipsis: [[fallthrough]]; case TT::HalfOpenEllipsis: return 475; @@ -461,14 +454,14 @@ namespace parser case TT::LogicalOr: return 350; - case TT::Equal: - case TT::PlusEq: - case TT::MinusEq: - case TT::MultiplyEq: - case TT::DivideEq: - case TT::ModEq: - case TT::AmpersandEq: - case TT::PipeEq: + case TT::Equal: [[fallthrough]]; + case TT::PlusEq: [[fallthrough]]; + case TT::MinusEq: [[fallthrough]]; + case TT::MultiplyEq: [[fallthrough]]; + case TT::DivideEq: [[fallthrough]]; + case TT::ModEq: [[fallthrough]]; + case TT::AmpersandEq: [[fallthrough]]; + case TT::PipeEq: [[fallthrough]]; case TT::CaretEq: return 100; diff --git a/source/frontend/parser/misc.cpp b/source/frontend/parser/misc.cpp index 03e4db86..bbf10265 100644 --- a/source/frontend/parser/misc.cpp +++ b/source/frontend/parser/misc.cpp @@ -100,7 +100,71 @@ namespace parser + AttribSet parseAttributes(State& st) + { + using UA = AttribSet::UserAttrib; + + if(st.front() <= TT::Attr_ATTRS_BEGIN || st.front() >= TT::Attr_ATTRS_END) + return AttribSet::of(attr::NONE); + + auto parseUA = [](State& st) -> UA { + + auto ret = UA(st.eat().str(), {}); + + if(st.front() == TT::LSquare) + { + auto begin = st.loc(); + + st.eat(); + + //* this means that attributes can only take tokens as arguments. if you want more complex stuff, + //* then it needs to be wrapped up in a string literal. + while(st.front() != TT::RSquare) + { + ret.args.push_back(st.eat().str()); + + if(st.front() == TT::Comma) + st.eat(); + + else if(st.front() != TT::RSquare) + expected(st.loc(), "']' to end argument list", st.prev().str()); + } + + iceAssert(st.front() == TT::RSquare); + st.pop(); + + if(ret.args.empty()) + warn(Location::unionOf(begin, st.ploc()), "empty argument list to attribute"); + } + + return ret; + }; + + + AttribSet ret; + while(st.front() > TT::Attr_ATTRS_BEGIN && st.front() < TT::Attr_ATTRS_END) + { + // i would love me some static reflection right now + switch(st.front()) + { + case TT::Attr_Raw: ret.set(attr::RAW); st.pop(); break; + case TT::Attr_NoMangle: ret.set(attr::NO_MANGLE); st.pop(); break; + case TT::Attr_EntryFn: ret.set(attr::FN_ENTRYPOINT); st.pop(); break; + case TT::Attr_Platform: unexpected(st.loc(), "@platform definition"); + case TT::Attr_Operator: unexpected(st.loc(), "@operator declaration"); + + default: + ret.add(parseUA(st)); + break; + } + } + + return ret; + } + + // TODO: switch this to the new attribute system. after the whole cddc19 shitshow @platform functionality + // TODO: remains unused. PlatformDefn* parsePlatformDefn(State& st) { iceAssert(st.front() == TT::Attr_Platform); diff --git a/source/frontend/parser/operators.cpp b/source/frontend/parser/operators.cpp index 74dbca98..620c98ef 100644 --- a/source/frontend/parser/operators.cpp +++ b/source/frontend/parser/operators.cpp @@ -158,6 +158,7 @@ namespace parser return ""; } + // TODO: move this over to the new attribute system if possible. size_t parseOperatorDecl(const lexer::TokenList& tokens, size_t idx, int* _kind, CustomOperatorDecl* out) { iceAssert(tokens[idx] == TT::Attr_Operator); diff --git a/source/include/ast.h b/source/include/ast.h index 5237e4e4..14838b46 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -36,7 +36,7 @@ namespace ast virtual ~Stmt(); virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) = 0; - AttributeSet attrs; + AttribSet attrs; }; struct Expr : Stmt diff --git a/source/include/codegen.h b/source/include/codegen.h index 0e9b93df..afeea5de 100644 --- a/source/include/codegen.h +++ b/source/include/codegen.h @@ -28,6 +28,7 @@ namespace sst struct TypeDefn; struct BinaryOp; struct StateTree; + struct FunctionDecl; struct FunctionDefn; struct FunctionCall; struct DefinitionTree; @@ -42,9 +43,6 @@ namespace cgn sst::Block* block = 0; - // std::vector refCountedValues; - // std::vector refCountedPointers; - fir::IRBlock* breakPoint = 0; fir::IRBlock* continuePoint = 0; }; @@ -88,6 +86,7 @@ namespace cgn util::hash_map methodList; util::hash_map typeDefnMap; + util::hash_map compilerSupportDefinitions; size_t _debugIRIndent = 0; @@ -188,6 +187,13 @@ namespace cgn void removeRAIIValue(fir::Value* val); std::vector getRAIIValues(); + sst::FunctionDefn* findMatchingMethodInType(sst::TypeDefn* td, sst::FunctionDecl* fn); + + bool isRAIIType(fir::Type* ty); + bool typeHasDestructor(fir::Type* ty); + bool typeHasCopyConstructor(fir::Type* ty); + bool typeHasMoveConstructor(fir::Type* ty); + void callDestructor(fir::Value* val); fir::Value* copyRAIIValue(fir::Value* value); @@ -195,7 +201,7 @@ namespace cgn void moveRAIIValue(fir::Value* from, fir::Value* target); }; - fir::Module* codegen(sst::DefinitionTree* __std_exception_destroy); + fir::Module* codegen(sst::DefinitionTree* dtree); } diff --git a/source/include/lexer.h b/source/include/lexer.h index 90f88a81..f5d8020a 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -118,6 +118,8 @@ namespace lexer Comment, EndOfFile, + Attr_ATTRS_BEGIN, + Attr_Raw, Attr_EntryFn, Attr_NoMangle, @@ -125,6 +127,8 @@ namespace lexer Attr_Platform, Attr_CompilerSupport, + Attr_ATTRS_END, + Directive_Run, Directive_If, }; diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 2cb8dda6..6833c113 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -318,6 +318,7 @@ namespace parser std::vector> parseGenericTypeList(State& st); PolyArgMapping_t parsePAMs(State& st, bool* failed); + AttribSet parseAttributes(State& st); std::tuple, std::vector>, pts::Type*, bool, Location> parseFunctionLookingDecl(State& st); diff --git a/source/include/sst_expr.h b/source/include/sst_expr.h index 435c0d4d..58ff7a64 100644 --- a/source/include/sst_expr.h +++ b/source/include/sst_expr.h @@ -26,7 +26,7 @@ namespace sst bool didCodegen = false; CGResult cachedResult = CGResult(0); - AttributeSet attrs; + AttribSet attrs; }; struct Expr : Stmt diff --git a/source/include/stcommon.h b/source/include/stcommon.h index e3754af5..d43698f5 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -17,47 +17,70 @@ namespace ast namespace attr { - constexpr uint32_t FN_ENTRYPOINT = 0x1; - constexpr uint32_t NO_MANGLE = 0x2; - constexpr uint32_t RAW = 0x4; - constexpr uint32_t COMPILER_SUPPORT = 0x8; + using FlagTy = uint32_t; + + constexpr FlagTy NONE = 0x0; + constexpr FlagTy FN_ENTRYPOINT = 0x1; + constexpr FlagTy NO_MANGLE = 0x2; + constexpr FlagTy RAW = 0x4; } -struct AttributeSet +struct AttribSet { + using FlagTy = attr::FlagTy; + struct UserAttrib { + UserAttrib(const std::string& name, const std::vector& args) : name(name), args(args) { } + std::string name; std::vector args; }; - std::vector userAttribs; - template - bool has(T x) const { return (this->flags & x); } - template - bool hasAny(T x) const { return (this->flags & x); } - - template - bool hasAny(T x, Args... xs) const { return (this->flags & x) || hasAny(xs...); } + bool has(const std::string& uaName) + { + return std::find_if(this->userAttribs.begin(), this->userAttribs.end(), [uaName](const UserAttrib& ua) -> bool { + return ua.name == uaName; + }) != this->userAttribs.end(); + } - template - bool hasAll(T x) const { return (this->flags & x); } + UserAttrib get(const std::string& uaName) + { + auto it = std::find_if(this->userAttribs.begin(), this->userAttribs.end(), [uaName](const UserAttrib& ua) -> bool { + return ua.name == uaName; + }); - template - bool hasAll(T x, Args... xs) const { return (this->flags & x) && hasAll(xs...); } + // would use optionals or something but lazy + if(it != this->userAttribs.end()) return *it; + else return UserAttrib("", {}); + } - template - void set(T x) { this->flags |= x; } + void set(FlagTy x) { this->flags |= x; } + bool has(FlagTy x) const { return (this->flags & x); } + bool hasAny(FlagTy x) const { return (this->flags & x); } + bool hasAll(FlagTy x) const { return (this->flags & x); } - template - void set(T x, Args... xs) { this->flags |= x; set(xs...); } + template void set(FlagTy x, Args... xs) { this->flags |= x; set(xs...); } + template bool hasAny(FlagTy x, Args... xs) const { return (this->flags & x) || hasAny(xs...); } + template bool hasAll(FlagTy x, Args... xs) const { return (this->flags & x) && hasAll(xs...); } + void add(const UserAttrib& ua) + { + this->userAttribs.push_back(ua); + } + static AttribSet of(FlagTy flags, const std::vector& attribs = {}) + { + return AttribSet { + .flags = flags, + .userAttribs = attribs + }; + } - private: - uint32_t flags; + FlagTy flags = attr::NONE; + std::vector userAttribs; }; diff --git a/source/include/typecheck.h b/source/include/typecheck.h index a8e4b0f1..d56d1f51 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -106,6 +106,7 @@ namespace sst std::unordered_set thingsImported; util::hash_map typeDefnMap; + util::hash_map compilerSupportDefinitions; }; struct TypecheckState diff --git a/source/include/utils.h b/source/include/utils.h index 6c7f78df..93c9382b 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -170,6 +170,30 @@ namespace util return ret; } + template + bool matchAny(const std::vector& input, Predicate cond) + { + for(const auto& x : input) + if(cond(x)) return true; + + return false; + } + + template + bool matchNone(const std::vector& input, Predicate cond) + { + return !matchAny(input, cond); + } + + template + bool matchAll(const std::vector& input, Predicate cond) + { + for(const auto& x : input) + if(!cond(x)) return false; + + return true; + } + template size_t indexOf(const std::vector& input, Predicate cond) { diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index e5f5dbef..f9324ba9 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -33,6 +33,7 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* auto defn = util::pool(this->loc); defn->bareName = this->name; + defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 5ea340ea..7c86fdfe 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -196,6 +196,7 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf defn->id = Identifier(this->name, IdKind::Name); defn->bareName = this->name; + defn->attrs = this->attrs; defn->params = ps; defn->returnType = retty; diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index 84f42745..b8213ddb 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -136,6 +136,7 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type auto defnname = util::typeParamMapToString(this->name, gmaps); auto defn = util::pool(this->loc); defn->bareName = this->name; + defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 5ef918ee..2c6d25a5 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -362,6 +362,11 @@ namespace sst fs->dtree->thingsImported.insert(ithing.name); fs->dtree->typeDefnMap.insert(import->typeDefnMap.begin(), import->typeDefnMap.end()); + + // merge the things. hopefully there are no conflicts???? + // TODO: check for conflicts! + fs->dtree->compilerSupportDefinitions.insert(import->compilerSupportDefinitions.begin(), + import->compilerSupportDefinitions.end()); } if(addPreludeDefinitions) @@ -414,11 +419,7 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer { // visit all functions first, to get out-of-order calling -- but only at the namespace level, not inside functions. // once we're in function-body-land, everything should be imperative-driven, and you shouldn't - // be able to see something before it is defined/declared. - - //* but, we need all types to be visible, so we can use them in the function declarations - //* we'll see next -- if not then it breaks - // visitTypes(fs, this, ret); + // be able to see something before it is defined/declared visitDeclarables(fs, this); } @@ -435,6 +436,19 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer else if(!tcr.isParametric() && !tcr.isDummy()) ret->statements.push_back(tcr.stmt()); + + + // check for compiler support so we can add it to the big list of things. + if(tcr.stmt()->attrs.has("@compiler_support")) + { + if(!tcr.isDefn()) + error(tcr.stmt(), "@compiler_support can only be applied to definitions"); + + auto ua = tcr.stmt()->attrs.get("@compiler_support"); + iceAssert(!ua.name.empty() && ua.args.size() == 1); + + fs->dtree->compilerSupportDefinitions[ua.args[0]] = tcr.defn(); + } } if(tree->parent) diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index b461fe91..865ff48d 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -24,6 +24,7 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* auto defn = util::pool(this->loc); defn->bareName = this->name; + defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; defn->visibility = this->visibility; @@ -126,6 +127,7 @@ static bool _checkFunctionTypesMatch(fir::Type* trait, fir::Type* type, fir::Fun auto ax = as[i]; auto bx = bs[i]; + // TODO: wtf is this doing?! if(ax != bx) { auto [ abase, atrfs ] = sst::poly::internal::decomposeIntoTransforms(ax, SIZE_MAX); diff --git a/source/typecheck/unions.cpp b/source/typecheck/unions.cpp index 1cb1dbaa..813d5237 100644 --- a/source/typecheck/unions.cpp +++ b/source/typecheck/unions.cpp @@ -34,6 +34,7 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* else defn = util::pool(this->loc); defn->bareName = this->name; + defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); defn->id.scope = this->realScope; From 774f27551643155c9420e59f0b69ed6c280a5977 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 15 Oct 2019 21:18:37 +0800 Subject: [PATCH 008/129] fix oversight --- source/typecheck/toplevel.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 2c6d25a5..9b3eaea9 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -437,9 +437,8 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer else if(!tcr.isParametric() && !tcr.isDummy()) ret->statements.push_back(tcr.stmt()); - // check for compiler support so we can add it to the big list of things. - if(tcr.stmt()->attrs.has("@compiler_support")) + if(tcr.isStmt() && tcr.stmt()->attrs.has("@compiler_support")) { if(!tcr.isDefn()) error(tcr.stmt(), "@compiler_support can only be applied to definitions"); From cafeb1ef9c6b557004dfdc0f349f50fa1d657c75 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 15 Oct 2019 22:26:35 +0800 Subject: [PATCH 009/129] fix bugs? --- source/codegen/assign.cpp | 4 +-- source/codegen/call.cpp | 2 ++ source/codegen/directives.cpp | 4 ++- source/codegen/variable.cpp | 1 + source/fir/IRBuilder.cpp | 52 +++++++++++++-------------- source/fir/Types/Type.cpp | 3 ++ source/typecheck/polymorph/driver.cpp | 1 + 7 files changed, 37 insertions(+), 30 deletions(-) diff --git a/source/codegen/assign.cpp b/source/codegen/assign.cpp index f663e53f..c4d29ed5 100644 --- a/source/codegen/assign.cpp +++ b/source/codegen/assign.cpp @@ -116,9 +116,7 @@ CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) rr = cs->oneWayAutocast(rr, lt); if(rr == 0) - { - error(this, "invalid assignment from value of type '%s' to expected type '%s'", rr->getType(), lt); - } + error(this, "invalid assignment from value of type '%s' to expected type '%s'", rt, lt); // ok then if(lt != rr->getType()) diff --git a/source/codegen/call.cpp b/source/codegen/call.cpp index b899f8fc..292415a3 100644 --- a/source/codegen/call.cpp +++ b/source/codegen/call.cpp @@ -414,6 +414,8 @@ CGResult sst::ExprCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(auto te = dcast(sst::TypeExpr, this->callee)) return callBuiltinTypeConstructor(cs, te->type, this->arguments); + iceAssert(this->callee); + fir::Value* fn = this->callee->codegen(cs).value; iceAssert(fn->getType()->isFunctionType()); diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp index bb2382be..b69e80e4 100644 --- a/source/codegen/directives.cpp +++ b/source/codegen/directives.cpp @@ -25,12 +25,14 @@ fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, s else retty = fir::Type::getVoid(); auto fn = cs->module->getOrCreateFunction(fname, fir::FunctionType::get({ }, retty), fir::LinkageType::Internal); + iceAssert(fn); + iceAssert(stmt); // make the function: { auto entry = cs->irb.addNewBlockInFunction("entry", fn); - cs->irb.setCurrentBlock(entry); + cs->irb.setCurrentBlock(entry); fir::Value* ret = 0; diff --git a/source/codegen/variable.cpp b/source/codegen/variable.cpp index e98cc920..bbf381f7 100644 --- a/source/codegen/variable.cpp +++ b/source/codegen/variable.cpp @@ -12,6 +12,7 @@ CGResult sst::VarDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) defer(cs->popLoc()); auto checkStore = [this, cs](fir::Value* val) -> fir::Value* { + iceAssert(val); fir::Value* nv = val; if(nv->getType() != this->type) diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 4ade495a..96aaf9b8 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -127,13 +127,13 @@ namespace fir // following c/c++ conventions, signed types are converted to unsigned types in mixed ops. if(lpt->getIntegerBitWidth() > rpt->getIntegerBitWidth()) { - if(lpt->isSigned() && rpt->isSigned()) out = lpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = lpt; + else out = (lpt->isSigned() ? rpt : lpt); } else { - if(lpt->isSigned() && rpt->isSigned()) out = rpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = rpt; + else out = (lpt->isSigned() ? rpt : lpt); } } } @@ -155,13 +155,13 @@ namespace fir // following c/c++ conventions, signed types are converted to unsigned types in mixed ops. if(lpt->getIntegerBitWidth() > rpt->getIntegerBitWidth()) { - if(lpt->isSigned() && rpt->isSigned()) out = lpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = lpt; + else out = (lpt->isSigned() ? rpt : lpt); } else { - if(lpt->isSigned() && rpt->isSigned()) out = rpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = rpt; + else out = (lpt->isSigned() ? rpt : lpt); } } } @@ -183,13 +183,13 @@ namespace fir // following c/c++ conventions, signed types are converted to unsigned types in mixed ops. if(lpt->getIntegerBitWidth() > rpt->getIntegerBitWidth()) { - if(lpt->isSigned() && rpt->isSigned()) out = lpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = lpt; + else out = (lpt->isSigned() ? rpt : lpt); } else { - if(lpt->isSigned() && rpt->isSigned()) out = rpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = rpt; + else out = (lpt->isSigned() ? rpt : lpt); } } } @@ -211,13 +211,13 @@ namespace fir // following c/c++ conventions, signed types are converted to unsigned types in mixed ops. if(lpt->getIntegerBitWidth() > rpt->getIntegerBitWidth()) { - if(lpt->isSigned() && rpt->isSigned()) out = lpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = lpt; + else out = (lpt->isSigned() ? rpt : lpt); } else { - if(lpt->isSigned() && rpt->isSigned()) out = rpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = rpt; + else out = (lpt->isSigned() ? rpt : lpt); } } } @@ -239,56 +239,56 @@ namespace fir // following c/c++ conventions, signed types are converted to unsigned types in mixed ops. if(lpt->getIntegerBitWidth() > rpt->getIntegerBitWidth()) { - if(lpt->isSigned() && rpt->isSigned()) out = lpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = lpt; + else out = (lpt->isSigned() ? rpt : lpt); } else { - if(lpt->isSigned() && rpt->isSigned()) out = rpt; - out = (lpt->isSigned() ? rpt : lpt); + if(lpt->isSigned() && rpt->isSigned()) out = rpt; + else out = (lpt->isSigned() ? rpt : lpt); } } } else if(ao == "<<") { if(useFloating) iceAssert("shift operation can only be done with ints"); - op = OpKind::Bitwise_Shl; + op = OpKind::Bitwise_Shl; out = lhs; } else if(ao == ">>") { if(useFloating) iceAssert("shift operation can only be done with ints"); - op = useSigned ? OpKind::Bitwise_Arithmetic_Shr : OpKind::Bitwise_Logical_Shr; + op = useSigned ? OpKind::Bitwise_Arithmetic_Shr : OpKind::Bitwise_Logical_Shr; out = lhs; } else if(ao == "&") { if(useFloating) iceAssert("bitwise ops only defined for int types (cast if needed)"); - op = OpKind::Bitwise_And; + op = OpKind::Bitwise_And; out = lhs; } else if(ao == "|") { if(useFloating) iceAssert("bitwise ops only defined for int types (cast if needed)"); - op = OpKind::Bitwise_Or; + op = OpKind::Bitwise_Or; out = lhs; } else if(ao == "^") { if(useFloating) iceAssert("bitwise ops only defined for int types (cast if needed)"); - op = OpKind::Bitwise_Xor; + op = OpKind::Bitwise_Xor; out = lhs; } else if(ao == "~") { if(useFloating) iceAssert("bitwise ops only defined for int types (cast if needed)"); - op = OpKind::Bitwise_Not; + op = OpKind::Bitwise_Not; out = lhs; } else diff --git a/source/fir/Types/Type.cpp b/source/fir/Types/Type.cpp index 41605a61..8dc8d262 100644 --- a/source/fir/Types/Type.cpp +++ b/source/fir/Types/Type.cpp @@ -1013,6 +1013,8 @@ namespace fir for(auto ty : tys) { auto a = getAlignmentOfType(ty); + iceAssert(a > 0); + if(ptr % a > 0) ptr += (a - (ptr % a)); @@ -1020,6 +1022,7 @@ namespace fir aln = std::max(aln, a); } + iceAssert(aln > 0); if(ptr % aln > 0) ptr += (aln - (ptr % aln)); diff --git a/source/typecheck/polymorph/driver.cpp b/source/typecheck/polymorph/driver.cpp index 23bdb51e..e9b0ab09 100644 --- a/source/typecheck/polymorph/driver.cpp +++ b/source/typecheck/polymorph/driver.cpp @@ -320,6 +320,7 @@ namespace poly } else { + iceAssert(thing); return std::make_pair(Solution_t(partial), SimpleError::make(thing->loc, "unable to infer type for unsupported entity '%s'", thing->getKind()) ); From 6b69ee5b1a58b7129570bcb0d9e143c7e7d5c084 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 16 Oct 2019 14:45:24 +0800 Subject: [PATCH 010/129] clean up strings and names etc. --- makefile | 2 +- source/codegen/builtin.cpp | 43 +++++++------ source/codegen/codegenstate.cpp | 2 +- source/codegen/dotop.cpp | 1 + source/codegen/misc.cpp | 2 +- source/codegen/raii.cpp | 47 +++++++------- source/fir/Module.cpp | 4 +- source/include/gluecode.h | 28 +------- source/include/string_consts.h | 76 ++++++++++++++++++++++ source/main.cpp | 110 ++++++++++++++++---------------- source/typecheck/dotop.cpp | 37 ++++++----- source/typecheck/toplevel.cpp | 5 +- 12 files changed, 208 insertions(+), 149 deletions(-) create mode 100644 source/include/string_consts.h diff --git a/makefile b/makefile index cc9c0b9d..9d7babbc 100644 --- a/makefile +++ b/makefile @@ -41,7 +41,7 @@ NUMFILES := $$(($(words $(CXXSRC)) + $(words $(CSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fexceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fno-exceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) CFLAGS += -std=c11 -O0 -g -c -Wall -fno-omit-frame-pointer -Wno-overlength-strings $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) diff --git a/source/codegen/builtin.cpp b/source/codegen/builtin.cpp index 119878f3..bbd01758 100644 --- a/source/codegen/builtin.cpp +++ b/source/codegen/builtin.cpp @@ -7,6 +7,9 @@ #include "gluecode.h" #include "typecheck.h" +// stupid c++, u don't do it with 'using' +namespace names = strs::names; + static fir::Value* checkNullPointerOrReturnZero(cgn::CodegenState* cs, fir::Value* ptr) { @@ -45,7 +48,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->isFunctionCall) { std::vector arguments = util::map(this->args, [cs](sst::Expr* e) -> fir::Value* { return e->codegen(cs).value; }); - if(this->name == BUILTIN_SAA_FN_CLONE) + if(this->name == names::saa::FN_CLONE) { iceAssert(arguments.empty()); auto clonef = cgn::glue::saa_common::generateCloneFunction(cs, ty); @@ -57,7 +60,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) return CGResult(ret); } - else if(this->name == BUILTIN_ARRAY_FN_POP) + else if(this->name == names::array::FN_POP) { iceAssert(!ty->isStringType()); @@ -79,7 +82,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) cs->irb.Store(newarr, res.value); return CGResult(retelm); } - else if(this->name == BUILTIN_SAA_FN_APPEND) + else if(this->name == names::saa::FN_APPEND) { iceAssert(arguments.size() == 1); @@ -107,20 +110,20 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { if(ty->isStringType() || ty->isDynamicArrayType()) { - if(this->name == BUILTIN_SAA_FIELD_POINTER) + if(this->name == names::saa::FIELD_POINTER) return CGResult(cs->irb.GetSAAData(res.value)); - else if(this->name == BUILTIN_SAA_FIELD_LENGTH) + else if(this->name == names::saa::FIELD_LENGTH) return CGResult(cs->irb.GetSAALength(res.value)); - else if(this->name == BUILTIN_SAA_FIELD_CAPACITY) + else if(this->name == names::saa::FIELD_CAPACITY) return CGResult(cs->irb.GetSAACapacity(res.value)); - else if(this->name == BUILTIN_SAA_FIELD_REFCOUNT) + else if(this->name == names::saa::FIELD_REFCOUNT) { return CGResult(checkNullPointerOrReturnZero(cs, cs->irb.GetSAARefCountPointer(res.value))); } - else if(ty->isStringType() && this->name == BUILTIN_STRING_FIELD_COUNT) + else if(ty->isStringType() && this->name == names::string::FIELD_COUNT) { auto fn = cgn::glue::string::getUnicodeLengthFunction(cs); iceAssert(fn); @@ -131,19 +134,19 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) } else if(ty->isArraySliceType()) { - if(this->name == BUILTIN_SAA_FIELD_LENGTH) + if(this->name == names::saa::FIELD_LENGTH) return CGResult(cs->irb.GetArraySliceLength(res.value)); - else if(this->name == BUILTIN_SAA_FIELD_POINTER) + else if(this->name == names::saa::FIELD_POINTER) return CGResult(cs->irb.GetArraySliceData(res.value)); } else if(ty->isArrayType()) { - if(this->name == BUILTIN_SAA_FIELD_LENGTH) + if(this->name == names::saa::FIELD_LENGTH) { return CGResult(fir::ConstantInt::getNative(ty->toArrayType()->getArraySize())); } - else if(this->name == BUILTIN_SAA_FIELD_POINTER) + else if(this->name == names::saa::FIELD_POINTER) { auto ret = cs->irb.ConstGEP2(res.value, 0, 0); return CGResult(ret); @@ -151,35 +154,35 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) } else if(ty->isRangeType()) { - if(this->name == BUILTIN_RANGE_FIELD_BEGIN) + if(this->name == names::range::FIELD_BEGIN) return CGResult(cs->irb.GetRangeLower(res.value)); - else if(this->name == BUILTIN_RANGE_FIELD_END) + else if(this->name == names::range::FIELD_END) return CGResult(cs->irb.GetRangeUpper(res.value)); - else if(this->name == BUILTIN_RANGE_FIELD_STEP) + else if(this->name == names::range::FIELD_STEP) return CGResult(cs->irb.GetRangeStep(res.value)); } else if(ty->isAnyType()) { - if(this->name == BUILTIN_ANY_FIELD_TYPEID) + if(this->name == names::any::FIELD_TYPEID) return CGResult(cs->irb.GetAnyTypeID(res.value)); - else if(this->name == BUILTIN_ANY_FIELD_REFCOUNT) + else if(this->name == names::any::FIELD_REFCOUNT) return CGResult(checkNullPointerOrReturnZero(cs, cs->irb.GetAnyRefCountPointer(res.value))); } else if(ty->isEnumType()) { - if(this->name == BUILTIN_ENUM_FIELD_INDEX) + if(this->name == names::enumeration::FIELD_INDEX) { return CGResult(cs->irb.GetEnumCaseIndex(res.value)); } - else if(this->name == BUILTIN_ENUM_FIELD_VALUE) + else if(this->name == names::enumeration::FIELD_VALUE) { return CGResult(cs->irb.GetEnumCaseValue(res.value)); } - else if(this->name == BUILTIN_ENUM_FIELD_NAME) + else if(this->name == names::enumeration::FIELD_NAME) { auto namearr = ty->toEnumType()->getNameArray(); iceAssert(namearr->getType()->isPointerType() && namearr->getType()->getPointerElementType()->isArrayType()); diff --git a/source/codegen/codegenstate.cpp b/source/codegen/codegenstate.cpp index 4ad845fc..c03df82e 100644 --- a/source/codegen/codegenstate.cpp +++ b/source/codegen/codegenstate.cpp @@ -356,7 +356,7 @@ namespace cgn if(!this->globalInitFunc) { - fir::Function* func = this->module->getOrCreateFunction(util::obfuscateIdentifier(BUILTIN_GLOBAL_INIT_FUNCTION_NAME), + fir::Function* func = this->module->getOrCreateFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); fir::IRBlock* entry = this->irb.addNewBlockInFunction("entry", func); diff --git a/source/codegen/dotop.cpp b/source/codegen/dotop.cpp index 8f136a16..64d665fe 100644 --- a/source/codegen/dotop.cpp +++ b/source/codegen/dotop.cpp @@ -7,6 +7,7 @@ #include "typecheck.h" #include "memorypool.h" + static bool isAutoDereferencable(fir::Type* t) { return (t->isStructType() || t->isClassType() || t->isRawUnionType()); diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index 7cb0f41d..cc336adf 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -50,7 +50,7 @@ sst::FunctionDefn* cgn::CodegenState::findMatchingMethodInType(sst::TypeDefn* td if(auto str = dcast(sst::StructDefn, td); str) { // TODO: when (if) we figure out what's going on in typecheck/traits.cpp:129, possibly change this to match. - auto it = std::find_if(str->methods.begin(), str->methods.end(), [this, fn](sst::FunctionDefn* method) -> bool { + auto it = std::find_if(str->methods.begin(), str->methods.end(), [fn](sst::FunctionDefn* method) -> bool { //* i think this check should work, `areMethodsVirtuallyCompatible` basically checks the parameters but takes //* co/contravariance into account and doesn't match the first (self) parameter. diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index b9070102..b63dcd44 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -4,7 +4,8 @@ #include "sst.h" #include "codegen.h" -#include "gluecode.h" +// #include "gluecode.h" +#include "string_consts.h" namespace cgn { @@ -93,24 +94,22 @@ namespace cgn // call the auto one. this will handle calling base class destructors for us! this->irb.Call(cls->getInlineDestructor(), selfptr); } - else + else if(auto it = this->compilerSupportDefinitions.find(strs::names::support::RAII_TRAIT_DROP); + it != this->compilerSupportDefinitions.end()) { - if(auto it = this->compilerSupportDefinitions.find("raii_trait::drop"); it != this->compilerSupportDefinitions.end()) - { - auto trt = dcast(sst::TraitDefn, it->second); - if(!trt) error("invalid use of @compiler_support[\"raii_trait::drop\"] on non-trait definition!"); + auto trt = dcast(sst::TraitDefn, it->second); + if(!trt) error("invalid use of @compiler_support[\"raii_trait::drop\"] on non-trait definition!"); - iceAssert(trt->methods.size() == 1); + iceAssert(trt->methods.size() == 1); - auto str = dcast(sst::StructDefn, this->typeDefnMap[val->getType()]); - iceAssert(str); + auto str = dcast(sst::StructDefn, this->typeDefnMap[val->getType()]); + iceAssert(str); - auto destructor = this->findMatchingMethodInType(str, trt->methods[0]); - if(destructor) - { - this->irb.Call(dcast(fir::Function, destructor->codegen(this).value), selfptr); - return; - } + auto destructor = this->findMatchingMethodInType(str, trt->methods[0]); + if(destructor) + { + this->irb.Call(dcast(fir::Function, destructor->codegen(this).value), selfptr); + return; } } } @@ -248,7 +247,7 @@ namespace cgn } - + // TODO: memoise this for each type; the typeHas-blalba ones also! static bool findRAIITraitImpl(CodegenState* cs, fir::Type* ty, const std::string& name) { if(ty->isClassType()) @@ -261,27 +260,27 @@ namespace cgn auto def = dcast(sst::StructDefn, cs->typeDefnMap[str]); iceAssert(def); - return util::matchAny(def->traits, [name](auto trt) -> bool { - return trt->id.name == name; + return util::matchAny(def->traits, [name](sst::TraitDefn* trt) -> bool { + // if we do not have such an attribute, then ::get returns an empty UA, + // with an empty string as a name. + return trt->attrs.get(strs::attrs::COMPILER_SUPPORT).name == strs::attrs::COMPILER_SUPPORT; }); } - // TODO: - //* after @compiler_support is implemented properly, these should probably use that mechanism to - //* find the name of the trait to search. + bool CodegenState::typeHasDestructor(fir::Type* ty) { - return findRAIITraitImpl(this, ty, "Drop"); + return findRAIITraitImpl(this, ty, strs::names::support::RAII_TRAIT_DROP); } bool CodegenState::typeHasCopyConstructor(fir::Type* ty) { - return findRAIITraitImpl(this, ty, "Copy"); + return findRAIITraitImpl(this, ty, strs::names::support::RAII_TRAIT_COPY); } bool CodegenState::typeHasMoveConstructor(fir::Type* ty) { - return findRAIITraitImpl(this, ty, "Move"); + return findRAIITraitImpl(this, ty, strs::names::support::RAII_TRAIT_MOVE); } bool CodegenState::isRAIIType(fir::Type* ty) diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index 99d765a2..00fe127e 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. -#include "gluecode.h" +#include "string_consts.h" #include "ir/module.h" #include "ir/irbuilder.h" @@ -53,7 +53,7 @@ namespace fir builder.setCurrentBlock(newentry); - auto gif = this->getFunction(util::obfuscateIdentifier(BUILTIN_GLOBAL_INIT_FUNCTION_NAME)); + auto gif = this->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION)); if(!gif) error("fir: failed to find global init function"); builder.Call(gif); diff --git a/source/include/gluecode.h b/source/include/gluecode.h index ce859bd2..566f8edf 100644 --- a/source/include/gluecode.h +++ b/source/include/gluecode.h @@ -10,6 +10,7 @@ #include #include "stcommon.h" +#include "string_consts.h" #define DEBUG_RUNTIME_GLUE_MASTER 0 @@ -25,36 +26,9 @@ #define DEBUG_ANY_ALLOCATION (1 & DEBUG_ANY_MASTER) #define DEBUG_ANY_REFCOUNTING (1 & DEBUG_ANY_MASTER) - -#define BUILTIN_SAA_FN_APPEND "append" -#define BUILTIN_SAA_FN_CLONE "clone" - -#define BUILTIN_SAA_FIELD_LENGTH "length" -#define BUILTIN_SAA_FIELD_POINTER "ptr" -#define BUILTIN_SAA_FIELD_REFCOUNT "refcount" -#define BUILTIN_SAA_FIELD_CAPACITY "capacity" - - -#define BUILTIN_STRING_FIELD_COUNT "count" - -#define BUILTIN_ARRAY_FN_POP "pop" - -#define BUILTIN_ENUM_FIELD_VALUE "value" -#define BUILTIN_ENUM_FIELD_INDEX "index" -#define BUILTIN_ENUM_FIELD_NAME "name" - -#define BUILTIN_RANGE_FIELD_BEGIN "begin" -#define BUILTIN_RANGE_FIELD_END "end" -#define BUILTIN_RANGE_FIELD_STEP "step" - -#define BUILTIN_ANY_FIELD_TYPEID "id" -#define BUILTIN_ANY_FIELD_REFCOUNT "refcount" - #define BUILTIN_ANY_DATA_BYTECOUNT 32 #define BUILTIN_ANY_FLAG_MASK 0x8000000000000000 -#define BUILTIN_GLOBAL_INIT_FUNCTION_NAME "global_init_function__" - namespace fir { diff --git a/source/include/string_consts.h b/source/include/string_consts.h new file mode 100644 index 00000000..b65dba9f --- /dev/null +++ b/source/include/string_consts.h @@ -0,0 +1,76 @@ +// string_consts.h +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#pragma once + +// is this too many levels of nesting? +namespace strs +{ + namespace attrs + { + inline constexpr auto COMPILER_SUPPORT = "@compiler_support"; + } + + namespace names + { + namespace saa + { + inline constexpr auto FN_APPEND = "append"; + inline constexpr auto FN_CLONE = "clone"; + + inline constexpr auto FIELD_LENGTH = "length"; + inline constexpr auto FIELD_POINTER = "ptr"; + inline constexpr auto FIELD_REFCOUNT = "refcount"; + inline constexpr auto FIELD_CAPACITY = "capacity"; + } + + namespace range + { + inline constexpr auto FIELD_BEGIN = "begin"; + inline constexpr auto FIELD_END = "end"; + inline constexpr auto FIELD_STEP = "step"; + } + + // obviously cos enum is a keyword + namespace enumeration + { + inline constexpr auto FIELD_VALUE = "value"; + inline constexpr auto FIELD_INDEX = "index"; + inline constexpr auto FIELD_NAME = "name"; + } + + namespace string + { + inline constexpr auto FIELD_COUNT = "count"; + } + + namespace array + { + inline constexpr auto FN_POP = "pop"; + } + + namespace any + { + inline constexpr auto FIELD_TYPEID = "id"; + inline constexpr auto FIELD_REFCOUNT = "refcount"; + } + + namespace support + { + inline constexpr auto RAII_TRAIT_DROP = "raii_trait::drop"; + inline constexpr auto RAII_TRAIT_COPY = "raii_trait::copy"; + inline constexpr auto RAII_TRAIT_MOVE = "raii_trait::move"; + } + + inline constexpr auto GLOBAL_INIT_FUNCTION = "global_init_function__"; + } +} + + + + + + + + diff --git a/source/main.cpp b/source/main.cpp index fe2fc5ed..5249e238 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -33,89 +33,87 @@ static void compile(std::string in, std::string out) { auto start_time = std::chrono::high_resolution_clock::now(); - double lexer_ms = 0; - double parser_ms = 0; + double lexer_ms = 0; + double parser_ms = 0; double typecheck_ms = 0; + double codegen_ms = 0; timer total; - frontend::CollectorState state; - sst::DefinitionTree* dtree = 0; + auto printStats = [&total](const std::string& name) { + if(frontend::getPrintProfileStats()) + { + debuglogln("%-9s (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", name, total.stop(), mem::getWatermark() / 1024.0, + mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); + } + }; + + + auto cd = backend::CompiledData(); { + frontend::CollectorState state; + sst::DefinitionTree* dtree = 0; + { timer t(&lexer_ms); frontend::collectFiles(in, &state); - - if(frontend::getPrintProfileStats()) - { - debuglogln("lex (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, - mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); - } + printStats("lex"); } { timer t(&parser_ms); frontend::parseFiles(&state); - - if(frontend::getPrintProfileStats()) - { - debuglogln("parse (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, - mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); - } + printStats("parse"); } { timer t(&typecheck_ms); dtree = frontend::typecheckFiles(&state); - - if(frontend::getPrintProfileStats()) - { - debuglogln("typechk (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, - mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); - } - - iceAssert(dtree); + printStats("typecheck"); } - } - timer t; + { + timer t(&codegen_ms); + iceAssert(dtree); + auto module = frontend::generateFIRModule(&state, dtree); + module->finaliseGlobalConstructors(); + printStats("codegen"); - fir::Module* module = frontend::generateFIRModule(&state, dtree); - module->finaliseGlobalConstructors(); + // if we requested to dump, then dump the IR here. + if(frontend::getPrintFIR()) + fprintf(stderr, "%s\n", module->print().c_str()); - auto cd = backend::CompiledData { module }; + cd.module = module; + } - if(frontend::getPrintProfileStats()) - { - debuglogln("codegen (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, - mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); - } - auto codegen_ms = t.stop(); + // delete *most* of the memory we've allocated. obviously IR values need to stay alive, + // since we haven't run the backend yet. so this just kills the AST and SST values. + util::clearAllPools(); + printStats("free_mem"); - // delete all the memory we've allocated. - util::clearAllPools(); - if(frontend::getPrintProfileStats()) - { - auto compile_ms = static_cast((std::chrono::high_resolution_clock::now() - start_time).count()) / 1000.0 / 1000.0; + // print the final stats. + if(frontend::getPrintProfileStats()) + { + auto compile_ms = static_cast((std::chrono::high_resolution_clock::now() - start_time).count()) / 1000.0 / 1000.0; - debuglogln("cleared (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", total.stop(), mem::getWatermark() / 1024.0, - mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); - debuglogln("compile (%.1f ms)\t[lex: %.1f, parse: %.1f, typechk: %.1f, codegen: %.1f]", - compile_ms, lexer_ms, parser_ms, typecheck_ms, codegen_ms); + debuglogln("%-9s (%.1f ms)\t[lex: %.1f, parse: %.1f, typechk: %.1f, codegen: %.1f]", "compile", + compile_ms, lexer_ms, parser_ms, typecheck_ms, codegen_ms); - debuglogln("%d lines, %.2f loc/s, %d fir values\n", state.totalLinesOfCode, - static_cast(state.totalLinesOfCode) / (compile_ms / 1000.0), - fir::Value::getCurrentValueId()); + debuglogln("processed: %d lines, %.2f loc/s, %d fir values\n", state.totalLinesOfCode, + static_cast(state.totalLinesOfCode) / (compile_ms / 1000.0), + fir::Value::getCurrentValueId()); + } } + iceAssert(cd.module); + + - if(frontend::getPrintFIR()) - fprintf(stderr, "%s\n", module->print().c_str()); { #if !OS_DARWIN @@ -128,19 +126,21 @@ static void compile(std::string in, std::string out) Backend* backend = Backend::getBackendFromOption(frontend::getBackendOption(), cd, { in }, out); if(backend == 0) return; - int capsneeded = 0; + int _capsneeded = 0; { if(frontend::getOutputMode() == ProgOutputMode::RunJit) - capsneeded |= BackendCaps::JIT; + _capsneeded |= BackendCaps::JIT; if(frontend::getOutputMode() == ProgOutputMode::ObjectFile) - capsneeded |= BackendCaps::EmitObject; + _capsneeded |= BackendCaps::EmitObject; if(frontend::getOutputMode() == ProgOutputMode::Program) - capsneeded |= BackendCaps::EmitProgram; + _capsneeded |= BackendCaps::EmitProgram; } + auto capsneeded = static_cast(_capsneeded); + - if(backend->hasCapability(static_cast(capsneeded))) + if(backend->hasCapability(capsneeded)) { backend->performCompilation(); backend->optimiseProgram(); @@ -149,7 +149,7 @@ static void compile(std::string in, std::string out) else { error("selected backend '%s' does not have some required capabilities (missing %s)\n", backend->str(), - capabilitiesToString(static_cast(capsneeded))); + capabilitiesToString(capsneeded)); } } } diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 4d312d12..697c4f21 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -14,6 +14,7 @@ #include "memorypool.h" +namespace names = strs::names; // make the nice message. @@ -243,11 +244,15 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { // TODO: Extension support here fir::Type* res = 0; - if(util::match(vr->name, BUILTIN_SAA_FIELD_LENGTH, BUILTIN_SAA_FIELD_CAPACITY, BUILTIN_SAA_FIELD_REFCOUNT, BUILTIN_STRING_FIELD_COUNT)) + if(util::match(vr->name, names::saa::FIELD_LENGTH, names::saa::FIELD_CAPACITY, + names::saa::FIELD_REFCOUNT, names::string::FIELD_COUNT)) + { res = fir::Type::getNativeWord(); - - else if(vr->name == BUILTIN_SAA_FIELD_POINTER) + } + else if(vr->name == names::saa::FIELD_POINTER) + { res = fir::Type::getInt8Ptr(); + } if(res) @@ -264,13 +269,13 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { fir::Type* res = 0; std::vector args; - if(fc->name == BUILTIN_SAA_FN_CLONE) + if(fc->name == names::saa::FN_CLONE) { res = type; if(fc->args.size() != 0) error(fc, "builtin string method 'clone' expects exactly 0 arguments, found %d instead", fc->args.size()); } - else if(fc->name == BUILTIN_SAA_FN_APPEND) + else if(fc->name == names::saa::FN_APPEND) { res = fir::Type::getVoid(); if(fc->args.size() != 1) @@ -307,12 +312,12 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(auto vr = dcast(ast::Ident, rhs)) { fir::Type* res = 0; - if(vr->name == BUILTIN_SAA_FIELD_LENGTH || (type->isDynamicArrayType() - && util::match(vr->name, BUILTIN_SAA_FIELD_CAPACITY, BUILTIN_SAA_FIELD_REFCOUNT))) + if(vr->name == names::saa::FIELD_LENGTH || (type->isDynamicArrayType() + && util::match(vr->name, names::saa::FIELD_CAPACITY, names::saa::FIELD_REFCOUNT))) { res = fir::Type::getNativeWord(); } - else if(vr->name == BUILTIN_SAA_FIELD_POINTER) + else if(vr->name == names::saa::FIELD_POINTER) { res = type->getArrayElementType()->getPointerTo(); if(type->isDynamicArrayType()) @@ -339,13 +344,13 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d fir::Type* res = 0; std::vector args; - if(fc->name == BUILTIN_SAA_FN_CLONE) + if(fc->name == names::saa::FN_CLONE) { res = type; if(fc->args.size() != 0) error(fc, "builtin array method 'clone' expects exactly 0 arguments, found %d instead", fc->args.size()); } - else if(fc->name == BUILTIN_SAA_FN_APPEND) + else if(fc->name == names::saa::FN_APPEND) { if(type->isArrayType()) error(fc, "'append' method cannot be called on arrays"); @@ -391,13 +396,13 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { // TODO: Extension support here fir::Type* res = 0; - if(vr->name == BUILTIN_ENUM_FIELD_NAME) + if(vr->name == names::enumeration::FIELD_NAME) res = fir::Type::getString(); - else if(vr->name == BUILTIN_ENUM_FIELD_INDEX) + else if(vr->name == names::enumeration::FIELD_INDEX) res = fir::Type::getNativeWord(); - else if(vr->name == BUILTIN_ENUM_FIELD_VALUE) + else if(vr->name == names::enumeration::FIELD_VALUE) res = type->toEnumType()->getCaseType(); @@ -419,7 +424,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { // TODO: extension support here fir::Type* res = 0; - if(vr->name == BUILTIN_RANGE_FIELD_BEGIN || vr->name == BUILTIN_RANGE_FIELD_END || vr->name == BUILTIN_RANGE_FIELD_STEP) + if(vr->name == names::range::FIELD_BEGIN || vr->name == names::range::FIELD_END || vr->name == names::range::FIELD_STEP) res = fir::Type::getNativeWord(); if(res) @@ -441,10 +446,10 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { // TODO: extension support here fir::Type* res = 0; - if(vr->name == BUILTIN_ANY_FIELD_TYPEID) + if(vr->name == names::any::FIELD_TYPEID) res = fir::Type::getNativeUWord(); - else if(vr->name == BUILTIN_ANY_FIELD_REFCOUNT) + else if(vr->name == names::any::FIELD_REFCOUNT) res = fir::Type::getNativeWord(); if(res) diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 9b3eaea9..b45b9c22 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -9,6 +9,7 @@ #include "parser.h" #include "frontend.h" #include "typecheck.h" +#include "string_consts.h" #include "ir/type.h" #include "memorypool.h" @@ -438,12 +439,12 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer ret->statements.push_back(tcr.stmt()); // check for compiler support so we can add it to the big list of things. - if(tcr.isStmt() && tcr.stmt()->attrs.has("@compiler_support")) + if((tcr.isStmt() || tcr.isDefn()) && tcr.stmt()->attrs.has(strs::attrs::COMPILER_SUPPORT)) { if(!tcr.isDefn()) error(tcr.stmt(), "@compiler_support can only be applied to definitions"); - auto ua = tcr.stmt()->attrs.get("@compiler_support"); + auto ua = tcr.stmt()->attrs.get(strs::attrs::COMPILER_SUPPORT); iceAssert(!ua.name.empty() && ua.args.size() == 1); fs->dtree->compilerSupportDefinitions[ua.args[0]] = tcr.defn(); From fec5ea8155f0ab08c0b587f441ebbf4b3815ec8e Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:01:10 +0800 Subject: [PATCH 011/129] smol test --- README.md | 13 + build/ultratiny.flx | 28 + external/stx/string_view.hpp | 729 ----------------------- external/tinyprocesslib/process_unix.inc | 4 +- external/utf8rewind/makefile | 22 + makefile | 36 +- source/backend/llvm/translator.cpp | 14 +- source/codegen/call.cpp | 4 +- source/codegen/literals.cpp | 2 +- source/codegen/raii.cpp | 4 +- source/codegen/refcounting.cpp | 14 +- source/fir/ConstantValue.cpp | 4 +- source/fir/IRBuilder.cpp | 8 +- source/fir/Instruction.cpp | 4 +- source/fir/Module.cpp | 4 +- source/fir/Types/ArrayType.cpp | 4 +- source/fir/Types/Type.cpp | 15 +- source/fir/interp/interpreter.cpp | 16 +- source/frontend/errors.cpp | 5 +- source/frontend/file.cpp | 24 +- source/frontend/lexer.cpp | 24 +- source/frontend/parser/literal.cpp | 4 +- source/include/defs.h | 101 ++-- source/include/errors.h | 7 +- source/include/frontend.h | 3 +- source/include/ir/type.h | 7 +- source/include/lexer.h | 14 +- source/include/parser_internal.h | 8 +- source/include/platform.h | 2 +- source/include/precompile.h | 19 +- source/include/zpr.h | 561 +++++++++++++++++ source/main.cpp | 16 +- source/misc/identifier.cpp | 51 +- source/platform/platform.cpp | 4 +- source/typecheck/dotop.cpp | 2 +- source/typecheck/literals.cpp | 4 +- source/typecheck/resolver/driver.cpp | 4 +- source/typecheck/resolver/resolver.cpp | 2 +- source/typecheck/toplevel.cpp | 3 +- source/typecheck/type.cpp | 2 + 40 files changed, 858 insertions(+), 934 deletions(-) delete mode 100644 external/stx/string_view.hpp create mode 100644 external/utf8rewind/makefile create mode 100644 source/include/zpr.h diff --git a/README.md b/README.md index 3a04b8ff..7a184cda 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,19 @@ welcome. +### Licensing + +The Flax compiler itself (this repository) is licensed under the Apache 2.0 license (see `LICENSE` file). For ease of building, some dependencies +are included in the repository itself (under the `external` folder) and compiled together, instead of as a separate library (shared or otherwise). +These are: + +1. [fmt](https://github.com/fmtlib/fmt), MIT +2. [mpreal](https://bitbucket.org/advanpix/mpreal), GPL +3. [tinyformat](https://github.com/c42f/tinyformat), Boost +4. [utf8rewind](https://bitbucket.org/knight666/utf8rewind), MIT +5. [flat_hash_map](https://github.com/skarupke/flat_hash_map), Boost +6. [tinyprocesslib](https://gitlab.com/eidheim/tiny-process-library), MIT + diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 3bd4dcbb..32dc6731 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -31,6 +31,34 @@ struct Foo : Drop, Copy, Move } } +/* + tinyformat: + 115.76 real 345.23 user 26.28 sys + 117.63 real 349.01 user 26.86 sys + 118.45 real 348.56 user 26.33 sys + 115.53 real 346.42 user 26.40 sys + 115.75 real 345.30 user 26.11 sys + +AVG: 116.62 real 346.90 user 26.39 sys + +---------------------------------------------------- + + zpr: + 101.56 real 340.92 user 22.60 sys + 102.43 real 341.59 user 22.76 sys + 99.60 real 337.48 user 22.46 sys + 100.78 real 340.38 user 22.63 sys + 102.06 real 341.57 user 22.75 sys + +AVG: 101.28 real 340.38 user 22.64 sys + +---------------------------------------------------- + + fmt: + 154.54 real 465.11 user 31.90 sys + 163.59 real 481.25 user 32.88 sys +*/ + class Bar { var data: int diff --git a/external/stx/string_view.hpp b/external/stx/string_view.hpp deleted file mode 100644 index be271bf0..00000000 --- a/external/stx/string_view.hpp +++ /dev/null @@ -1,729 +0,0 @@ -/* - � Copyright (c) Marshall Clow 2012-2015. - � Copyright Beman Dawes 2015 - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - - For more information, see http://www.boost.org - - Based on the StringRef implementation in LLVM (http://llvm.org) and - N3422 by Jeffrey Yasskin - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html - Updated July 2015 to reflect the Library Fundamentals TS - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4480.html -*/ - -#ifndef STX_STRING_VIEW_HPP_INCLUDED -#define STX_STRING_VIEW_HPP_INCLUDED - -#ifndef STX_NAMESPACE_NAME -#define STX_NAMESPACE_NAME stx -#endif - -// libstdc++'s std::experimental::string_view requries C++14 -#if !defined(STX_NO_STD_STRING_VIEW) && (__cplusplus < 201402) -#include -#if defined(__GLIBCXX__) -#define STX_NO_STD_STRING_VIEW -#endif -#endif - -#if defined(__has_include) && !defined(STX_NO_STD_STRING_VIEW) -#if __has_include() && (__cplusplus > 201402) - #include - #define STX_HAVE_STD_STRING_VIEW 1 - namespace STX_NAMESPACE_NAME { - using std::basic_string_view; - using std::string_view; - using std::u16string_view; - using std::u32string_view; - using std::wstring_view; - } - #elif __has_include() - #include - #define STX_HAVE_STD_STRING_VIEW 1 - namespace STX_NAMESPACE_NAME { - using std::experimental::basic_string_view; - using std::experimental::string_view; - using std::experimental::u16string_view; - using std::experimental::u32string_view; - using std::experimental::wstring_view; - } - #endif // (__has_include() || __has_include()) -#endif // __has_include - -#ifndef STX_HAVE_STD_STRING_VIEW - -#include -#include -#include -#include -#include -#include -#include - -#if __cpp_constexpr >= 201304 -#define STX_CONSTEXPR14 constexpr -#else -#define STX_CONSTEXPR14 -#endif - -namespace STX_NAMESPACE_NAME { - -namespace detail { -// A helper functor because sometimes we don't have lambdas -template -class string_view_traits_eq { -public: - string_view_traits_eq ( charT ch ) : ch_(ch) {} - bool operator()( charT val ) const { return traits::eq (ch_, val); } - charT ch_; -}; -} - -template> -class basic_string_view { -public: - // types - typedef traits traits_type; - typedef charT value_type; - typedef charT* pointer; - typedef const charT* const_pointer; - typedef charT& reference; - typedef const charT& const_reference; - typedef const_pointer const_iterator; // impl-defined - typedef const_iterator iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef const_reverse_iterator reverse_iterator; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - static constexpr size_type npos = size_type(-1); - - // construct/copy - constexpr basic_string_view() noexcept - : ptr_(NULL), len_(0) {} - - // by defaulting these functions, basic_string_ref becomes - // trivially copy/move constructible. - constexpr basic_string_view(const basic_string_view &rhs) noexcept = default; -#if 0 - : ptr_(rhs.ptr_), len_(rhs.len_) {} -#endif - - basic_string_view& operator=(const basic_string_view &rhs) noexcept - = default; -#if 0 - { - ptr_ = rhs.ptr_; - len_ = rhs.len_; - return *this; - } -#endif - - template - basic_string_view(const std::basic_string& str) noexcept - : ptr_(str.data()), len_(str.length()) {} - - constexpr basic_string_view(const charT* str) - : ptr_(str), len_(traits::length(str)) {} - - constexpr basic_string_view(const charT* str, size_type len) - : ptr_(str), len_(len) {} - - // iterators - constexpr const_iterator begin() const noexcept { return ptr_; } - constexpr const_iterator cbegin() const noexcept { return ptr_; } - constexpr const_iterator end() const noexcept { return ptr_ + len_; } - constexpr const_iterator cend() const noexcept { return ptr_ + len_; } - const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } - const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } - - // capacity - constexpr size_type size() const noexcept { return len_; } - constexpr size_type length() const noexcept { return len_; } - constexpr size_type max_size() const noexcept { return len_; } - constexpr bool empty() const noexcept { return len_ == 0; } - - // element access - constexpr const_reference operator[](size_type pos) const noexcept { return ptr_[pos]; } - - constexpr const_reference at(size_t pos) const { - return pos >= len_ ? throw std::out_of_range("stx::string_view::at") : ptr_[pos]; -// if ( pos >= len_ ) -// BOOST_THROW_EXCEPTION( std::out_of_range ( "stx::string_view::at" ) ); -// return ptr_[pos]; - } - - constexpr const_reference front() const { return ptr_[0]; } - constexpr const_reference back() const { return ptr_[len_-1]; } - constexpr const_pointer data() const noexcept { return ptr_; } - - // modifiers - void clear() noexcept { len_ = 0; } // Boost extension - - STX_CONSTEXPR14 void remove_prefix(size_type n) { - if ( n > len_ ) - n = len_; - ptr_ += n; - len_ -= n; - } - - STX_CONSTEXPR14 void remove_suffix(size_type n) { - if ( n > len_ ) - n = len_; - len_ -= n; - } - - STX_CONSTEXPR14 void swap(basic_string_view& s) noexcept { - std::swap(ptr_, s.ptr_); - std::swap(len_, s.len_); - } - - // basic_string_view string operations -#ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS - template - explicit operator std::basic_string() const { - return std::basic_string(begin(), end()); - } -#endif - -#ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS - template > - std::basic_string to_string(const Allocator& a = Allocator()) const { - return std::basic_string(begin(), end(), a); - } -#else - std::basic_string to_string() const { - return std::basic_string(begin(), end()); - } - - template - std::basic_string to_string(const Allocator& a) const { - return std::basic_string(begin(), end(), a); - } -#endif - - size_type copy(charT* s, size_type n, size_type pos=0) const { - if (pos > size()) - throw std::out_of_range("string_view::copy" ); - size_type rlen = (std::min)(n, len_ - pos); - // use std::copy(begin() + pos, begin() + pos + rlen, s) rather than - // std::copy_n(begin() + pos, rlen, s) to support pre-C++11 standard libraries - std::copy(begin() + pos, begin() + pos + rlen, s); - return rlen; - } - - STX_CONSTEXPR14 basic_string_view substr(size_type pos, size_type n=npos) const { - if ( pos > size()) - throw std::out_of_range ( "string_view::substr" ); - if (n == npos || pos + n > size()) - n = size () - pos; - return basic_string_view(data() + pos, n); - } - - STX_CONSTEXPR14 int compare(basic_string_view x) const noexcept { - const int cmp = traits::compare(ptr_, x.ptr_, (std::min)(len_, x.len_)); - return cmp != 0 ? cmp : (len_ == x.len_ ? 0 : len_ < x.len_ ? -1 : 1); - } - - STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, basic_string_view x) - const noexcept { - return substr(pos1, n1).compare(x); - } - - STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, - basic_string_view x, size_type pos2, size_type n2) const { - return substr(pos1, n1).compare(x.substr(pos2, n2)); - } - - STX_CONSTEXPR14 int compare(const charT* x) const { - return compare(basic_string_view(x)); - } - - STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, const charT* x) const { - return substr(pos1, n1).compare(basic_string_view(x)); - } - - STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, - const charT* x, size_type n2) const { - return substr(pos1, n1).compare(basic_string_view(x, n2)); - } - - // Searches - constexpr bool starts_with(charT c) const noexcept { // Boost extension - return !empty() && traits::eq(c, front()); - } - - constexpr bool starts_with(basic_string_view x) const noexcept { // Boost extension - return len_ >= x.len_ && traits::compare(ptr_, x.ptr_, x.len_) == 0; - } - - constexpr bool ends_with(charT c) const noexcept { // Boost extension - return !empty() && traits::eq(c, back()); - } - - constexpr bool ends_with(basic_string_view x) const noexcept { // Boost extension - return len_ >= x.len_ && - traits::compare(ptr_ + len_ - x.len_, x.ptr_, x.len_) == 0; - } - - // find - STX_CONSTEXPR14 size_type find(basic_string_view s, size_type pos = 0) const noexcept { - if (pos > size()) - return npos; - if (s.empty()) - return pos; - const_iterator iter = std::search(this->cbegin() + pos, this->cend(), - s.cbegin (), s.cend (), traits::eq); - return iter == this->cend () ? npos : std::distance(this->cbegin (), iter); - } - STX_CONSTEXPR14 size_type find(charT c, size_type pos = 0) const noexcept - { return find(basic_string_view(&c, 1), pos); } - STX_CONSTEXPR14 size_type find(const charT* s, size_type pos, size_type n) const noexcept - { return find(basic_string_view(s, n), pos); } - STX_CONSTEXPR14 size_type find(const charT* s, size_type pos = 0) const noexcept - { return find(basic_string_view(s), pos); } - - // rfind - STX_CONSTEXPR14 size_type rfind(basic_string_view s, size_type pos = npos) const noexcept { - if (len_ < s.len_) - return npos; - if (pos > len_ - s.len_) - pos = len_ - s.len_; - if (s.len_ == 0u) // an empty string is always found - return pos; - for (const charT* cur = ptr_ + pos; ; --cur) { - if (traits::compare(cur, s.ptr_, s.len_) == 0) - return cur - ptr_; - if (cur == ptr_) - return npos; - }; - } - STX_CONSTEXPR14 size_type rfind(charT c, size_type pos = npos) const noexcept - { return rfind(basic_string_view(&c, 1), pos); } - STX_CONSTEXPR14 size_type rfind(const charT* s, size_type pos, size_type n) const noexcept - { return rfind(basic_string_view(s, n), pos); } - STX_CONSTEXPR14 size_type rfind(const charT* s, size_type pos = npos) const noexcept - { return rfind(basic_string_view(s), pos); } - - // find_first_of - STX_CONSTEXPR14 size_type find_first_of(basic_string_view s, size_type pos = 0) const noexcept { - if (pos >= len_ || s.len_ == 0) - return npos; - const_iterator iter = std::find_first_of - (this->cbegin () + pos, this->cend (), s.cbegin (), s.cend (), traits::eq); - return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); - } - STX_CONSTEXPR14 size_type find_first_of(charT c, size_type pos = 0) const noexcept - { return find_first_of(basic_string_view(&c, 1), pos); } - STX_CONSTEXPR14 size_type find_first_of(const charT* s, size_type pos, size_type n) const noexcept - { return find_first_of(basic_string_view(s, n), pos); } - STX_CONSTEXPR14 size_type find_first_of(const charT* s, size_type pos = 0) const noexcept - { return find_first_of(basic_string_view(s), pos); } - - // find_last_of - STX_CONSTEXPR14 size_type find_last_of(basic_string_view s, size_type pos = npos) const noexcept { - if (s.len_ == 0u) - return npos; - if (pos >= len_) - pos = 0; - else - pos = len_ - (pos+1); - const_reverse_iterator iter = std::find_first_of - ( this->crbegin () + pos, this->crend (), s.cbegin (), s.cend (), traits::eq ); - return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter); - } - STX_CONSTEXPR14 size_type find_last_of(charT c, size_type pos = npos) const noexcept - { return find_last_of(basic_string_view(&c, 1), pos); } - STX_CONSTEXPR14 size_type find_last_of(const charT* s, size_type pos, size_type n) const noexcept - { return find_last_of(basic_string_view(s, n), pos); } - STX_CONSTEXPR14 size_type find_last_of(const charT* s, size_type pos = npos) const noexcept - { return find_last_of(basic_string_view(s), pos); } - - // find_first_not_of - STX_CONSTEXPR14 size_type find_first_not_of(basic_string_view s, size_type pos = 0) const noexcept { - if (pos >= len_) - return npos; - if (s.len_ == 0) - return pos; - const_iterator iter = find_not_of ( this->cbegin () + pos, this->cend (), s ); - return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); - } - STX_CONSTEXPR14 size_type find_first_not_of(charT c, size_type pos = 0) const noexcept - { return find_first_not_of(basic_string_view(&c, 1), pos); } - STX_CONSTEXPR14 size_type find_first_not_of(const charT* s, size_type pos, size_type n) const noexcept - { return find_first_not_of(basic_string_view(s, n), pos); } - STX_CONSTEXPR14 size_type find_first_not_of(const charT* s, size_type pos = 0) const noexcept - { return find_first_not_of(basic_string_view(s), pos); } - - // find_last_not_of - STX_CONSTEXPR14 size_type find_last_not_of(basic_string_view s, size_type pos = npos) const noexcept { - if (pos >= len_) - pos = len_ - 1; - if (s.len_ == 0u) - return pos; - pos = len_ - (pos+1); - const_reverse_iterator iter = find_not_of ( this->crbegin () + pos, this->crend (), s ); - return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); - } - STX_CONSTEXPR14 size_type find_last_not_of(charT c, size_type pos = npos) const noexcept - { return find_last_not_of(basic_string_view(&c, 1), pos); } - STX_CONSTEXPR14 size_type find_last_not_of(const charT* s, size_type pos, size_type n) const noexcept - { return find_last_not_of(basic_string_view(s, n), pos); } - STX_CONSTEXPR14 size_type find_last_not_of(const charT* s, size_type pos = npos) const noexcept - { return find_last_not_of(basic_string_view(s), pos); } - -private: - template - size_type reverse_distance(r_iter first, r_iter last) const noexcept { - // Portability note here: std::distance is not NOEXCEPT, but calling it with a string_view::reverse_iterator will not throw. - return len_ - 1 - std::distance ( first, last ); - } - - template - Iterator find_not_of(Iterator first, Iterator last, basic_string_view s) const noexcept { - for (; first != last ; ++first) - if ( 0 == traits::find(s.ptr_, s.len_, *first)) - return first; - return last; - } - - const charT *ptr_; - std::size_t len_; -}; - - -// Comparison operators -// Equality -template -inline bool operator==(basic_string_view x, - basic_string_view y) noexcept { - if (x.size () != y.size ()) return false; - return x.compare(y) == 0; -} - -// Inequality -template -inline bool operator!=(basic_string_view x, - basic_string_view y) noexcept { - if ( x.size () != y.size ()) return true; - return x.compare(y) != 0; -} - -// Less than -template -inline bool operator<(basic_string_view x, - basic_string_view y) noexcept { - return x.compare(y) < 0; -} - -// Greater than -template -inline bool operator>(basic_string_view x, - basic_string_view y) noexcept { - return x.compare(y) > 0; -} - -// Less than or equal to -template -inline bool operator<=(basic_string_view x, - basic_string_view y) noexcept { - return x.compare(y) <= 0; -} - -// Greater than or equal to -template -inline bool operator>=(basic_string_view x, - basic_string_view y) noexcept { - return x.compare(y) >= 0; -} - -// "sufficient additional overloads of comparison functions" -template -inline bool operator==(basic_string_view x, - const std::basic_string & y) noexcept { - return x == basic_string_view(y); -} - -template -inline bool operator==(const std::basic_string & x, - basic_string_view y) noexcept { - return basic_string_view(x) == y; -} - -template -inline bool operator==(basic_string_view x, - const charT * y) noexcept { - return x == basic_string_view(y); -} - -template -inline bool operator==(const charT * x, - basic_string_view y) noexcept { - return basic_string_view(x) == y; -} - -template -inline bool operator!=(basic_string_view x, - const std::basic_string & y) noexcept { - return x != basic_string_view(y); -} - -template -inline bool operator!=(const std::basic_string & x, - basic_string_view y) noexcept { - return basic_string_view(x) != y; -} - -template -inline bool operator!=(basic_string_view x, - const charT * y) noexcept { - return x != basic_string_view(y); -} - -template -inline bool operator!=(const charT * x, - basic_string_view y) noexcept { - return basic_string_view(x) != y; -} - -template -inline bool operator<(basic_string_view x, - const std::basic_string & y) noexcept { - return x < basic_string_view(y); -} - -template -inline bool operator<(const std::basic_string & x, - basic_string_view y) noexcept { - return basic_string_view(x) < y; -} - -template -inline bool operator<(basic_string_view x, - const charT * y) noexcept { - return x < basic_string_view(y); -} - -template -inline bool operator<(const charT * x, - basic_string_view y) noexcept { - return basic_string_view(x) < y; -} - -template -inline bool operator>(basic_string_view x, - const std::basic_string & y) noexcept { - return x > basic_string_view(y); -} - -template -inline bool operator>(const std::basic_string & x, - basic_string_view y) noexcept { - return basic_string_view(x) > y; -} - -template -inline bool operator>(basic_string_view x, - const charT * y) noexcept { - return x > basic_string_view(y); -} - -template -inline bool operator>(const charT * x, - basic_string_view y) noexcept { - return basic_string_view(x) > y; -} - -template -inline bool operator<=(basic_string_view x, - const std::basic_string & y) noexcept { - return x <= basic_string_view(y); -} - -template -inline bool operator<=(const std::basic_string & x, - basic_string_view y) noexcept { - return basic_string_view(x) <= y; -} - -template -inline bool operator<=(basic_string_view x, - const charT * y) noexcept { - return x <= basic_string_view(y); -} - -template -inline bool operator<=(const charT * x, - basic_string_view y) noexcept { - return basic_string_view(x) <= y; -} - -template -inline bool operator>=(basic_string_view x, - const std::basic_string & y) noexcept { - return x >= basic_string_view(y); -} - -template -inline bool operator>=(const std::basic_string & x, - basic_string_view y) noexcept { - return basic_string_view(x) >= y; -} - -template -inline bool operator>=(basic_string_view x, - const charT * y) noexcept { - return x >= basic_string_view(y); -} - -template -inline bool operator>=(const charT * x, - basic_string_view y) noexcept { - return basic_string_view(x) >= y; -} - -namespace detail { - -template -inline void sv_insert_fill_chars(std::basic_ostream& os, std::size_t n) { - enum { chunk_size = 8 }; - charT fill_chars[chunk_size]; - std::fill_n(fill_chars, static_cast< std::size_t >(chunk_size), os.fill()); - for (; n >= chunk_size && os.good(); n -= chunk_size) - os.write(fill_chars, static_cast< std::size_t >(chunk_size)); - if (n > 0 && os.good()) - os.write(fill_chars, n); -} - -template -void sv_insert_aligned(std::basic_ostream& os, const basic_string_view& str) { - const std::size_t size = str.size(); - const std::size_t alignment_size = static_cast< std::size_t >(os.width()) - size; - const bool align_left = (os.flags() & std::basic_ostream::adjustfield) == std::basic_ostream::left; - if (!align_left) { - detail::sv_insert_fill_chars(os, alignment_size); - if (os.good()) - os.write(str.data(), size); - } - else { - os.write(str.data(), size); - if (os.good()) - detail::sv_insert_fill_chars(os, alignment_size); - } -} - -} // namespace detail - -// Inserter -template -inline std::basic_ostream& -operator<<(std::basic_ostream& os, - const basic_string_view& str) { - if (os.good()) { - const std::size_t size = str.size(); - const std::size_t w = static_cast< std::size_t >(os.width()); - if (w <= size) - os.write(str.data(), size); - else - detail::sv_insert_aligned(os, str); - os.width(0); - } - return os; -} - -#if 0 -// numeric conversions - // - // These are short-term implementations. - // In a production environment, I would rather avoid the copying. - // - inline int stoi (string_view str, size_t* idx=0, int base=10) { - return std::stoi ( std::string(str), idx, base ); - } - - inline long stol (string_view str, size_t* idx=0, int base=10) { - return std::stol ( std::string(str), idx, base ); - } - - inline unsigned long stoul (string_view str, size_t* idx=0, int base=10) { - return std::stoul ( std::string(str), idx, base ); - } - - inline long long stoll (string_view str, size_t* idx=0, int base=10) { - return std::stoll ( std::string(str), idx, base ); - } - - inline unsigned long long stoull (string_view str, size_t* idx=0, int base=10) { - return std::stoull ( std::string(str), idx, base ); - } - - inline float stof (string_view str, size_t* idx=0) { - return std::stof ( std::string(str), idx ); - } - - inline double stod (string_view str, size_t* idx=0) { - return std::stod ( std::string(str), idx ); - } - - inline long double stold (string_view str, size_t* idx=0) { - return std::stold ( std::string(str), idx ); - } - - inline int stoi (wstring_view str, size_t* idx=0, int base=10) { - return std::stoi ( std::wstring(str), idx, base ); - } - - inline long stol (wstring_view str, size_t* idx=0, int base=10) { - return std::stol ( std::wstring(str), idx, base ); - } - - inline unsigned long stoul (wstring_view str, size_t* idx=0, int base=10) { - return std::stoul ( std::wstring(str), idx, base ); - } - - inline long long stoll (wstring_view str, size_t* idx=0, int base=10) { - return std::stoll ( std::wstring(str), idx, base ); - } - - inline unsigned long long stoull (wstring_view str, size_t* idx=0, int base=10) { - return std::stoull ( std::wstring(str), idx, base ); - } - - inline float stof (wstring_view str, size_t* idx=0) { - return std::stof ( std::wstring(str), idx ); - } - - inline double stod (wstring_view str, size_t* idx=0) { - return std::stod ( std::wstring(str), idx ); - } - - inline long double stold (wstring_view str, size_t* idx=0) { - return std::stold ( std::wstring(str), idx ); - } -#endif - -using string_view = basic_string_view>; -using u16string_view = basic_string_view>; -using u32string_view = basic_string_view>; -using wstring_view = basic_string_view>; - -} // end namespace - -#if 0 -namespace std { - // Hashing - template<> struct hash; - template<> struct hash; - template<> struct hash; - template<> struct hash; -} -#endif - -#endif // STX_HAVE_STD_STRING_VIEW - -#endif // STX_STRING_VIEW_HPP_INCLUDED diff --git a/external/tinyprocesslib/process_unix.inc b/external/tinyprocesslib/process_unix.inc index 3d68552b..2e4c637d 100644 --- a/external/tinyprocesslib/process_unix.inc +++ b/external/tinyprocesslib/process_unix.inc @@ -223,7 +223,9 @@ namespace tinyproclib bool Process::write(const char* bytes, size_t n) { if(!open_stdin) - throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process."); + return false; + + // throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process."); std::lock_guard lock(stdin_mutex); if(stdin_fd) diff --git a/external/utf8rewind/makefile b/external/utf8rewind/makefile new file mode 100644 index 00000000..58c50a00 --- /dev/null +++ b/external/utf8rewind/makefile @@ -0,0 +1,22 @@ +# bleugh. +# so we can call from our main makefile. + +CSRC := $(shell find source -name "*.c") +COBJ := $(CSRC:.c=.c.o) + +.PHONY: all + +all: ../libutf8rewind.a + @: + +../libutf8rewind.a: $(COBJ) + @echo "# libutf8rewind" + @$(AR) rcs ../libutf8rewind.a $(COBJ) + +%.cc.o: %.cc + @echo "# fmt/$(notdir $<)" + @$(CXX) -std=c11 -O3 -c -Iinclude -o $@ $< + + + + diff --git a/makefile b/makefile index 9d7babbc..924e467c 100644 --- a/makefile +++ b/makefile @@ -22,10 +22,7 @@ LLVM_CONFIG ?= "llvm-config" CXXSRC := $(shell find source external -iname "*.cpp") -CSRC := $(shell find source external -iname "*.c") - CXXOBJ := $(CXXSRC:.cpp=.cpp.o) -COBJ := $(CSRC:.c=.c.o) PRECOMP_HDRS := source/include/precompile.h PRECOMP_GCH := $(PRECOMP_HDRS:.h=.h.gch) @@ -35,8 +32,7 @@ FLXSRC := $(shell find libs -iname "*.flx") CXXDEPS := $(CXXSRC:.cpp=.cpp.d) - -NUMFILES := $$(($(words $(CXXSRC)) + $(words $(CSRC)))) +NUMFILES := $$(($(words $(CXXSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := @@ -46,16 +42,7 @@ CFLAGS += -std=c11 -O0 -g -c -Wall -fno-omit-frame-pointer -Wno-overlength-str LDFLAGS += $(SANITISE) -FLXFLAGS += -sysroot $(SYSROOT) --ffi-escape - -SUPERTINYBIN := build/supertiny -GLTESTBIN := build/gltest -TESTBIN := build/tester - -SUPERTINYSRC := build/supertiny.flx -GLTESTSRC := build/gltest.flx -TESTSRC := build/tester.flx UNAME_IDENT := $(shell uname) COMPILER_IDENT := $(shell $(CC) --version | head -n 1) @@ -86,6 +73,20 @@ ifneq (,$(findstring clang,$(COMPILER_IDENT))) endif +UTF8REWIND_AR := external/libutf8rewind.a + + +FLXFLAGS += -sysroot $(SYSROOT) --ffi-escape + +SUPERTINYBIN := build/supertiny +GLTESTBIN := build/gltest +TESTBIN := build/tester + +SUPERTINYSRC := build/supertiny.flx +GLTESTSRC := build/gltest.flx +TESTSRC := build/tester.flx + + .DEFAULT_GOAL = jit -include $(CXXDEPS) @@ -129,10 +130,10 @@ copylibs: $(FLXSRC) @mv $(FLXLIBLOCATION)/libs $(FLXLIBLOCATION)/flaxlibs -$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) +$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp $(LDFLAGS) -lpthread -ldl -lffi + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind -lfmt %.cpp.o: %.cpp @@ -140,6 +141,9 @@ $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) @printf "# compiling [$(words $(DONEFILES))/$(NUMFILES)] $<\n" @$(CXX) $(CXXFLAGS) $(WARNINGS) -include source/include/precompile.h -Isource/include -Iexternal -I$(shell $(LLVM_CONFIG) --includedir) -MMD -MP -o $@ $< +$(UTF8REWIND_AR): + @make -C external/utf8rewind all + %.c.o: %.c @$(eval DONEFILES += "C") diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 9c0255ec..790e5ec1 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -581,7 +581,7 @@ namespace backend { llvm::Value* lgv = valueMap[gv->id]; if(!lgv) - error("llvm: failed to find var %zu in mod %s\n", gv->id, firmod->getModuleName()); + error("llvm: failed to find var %d in mod %s\n", gv->id, firmod->getModuleName()); iceAssert(lgv); return lgv; @@ -590,7 +590,7 @@ namespace backend else if(dcast(fir::Function, fv)) { llvm::Value* ret = valueMap[fv->id]; - if(!ret) error("llvm: !ret fn (id = %zu)", fv->id); + if(!ret) error("llvm: !ret fn (id = %d)", fv->id); return ret; } else if(fir::ConstantValue* cv = dcast(fir::ConstantValue, fv)) @@ -600,7 +600,7 @@ namespace backend else { llvm::Value* ret = valueMap[fv->id]; - if(!ret) error("llvm: !ret (id = %zu)", fv->id); + if(!ret) error("llvm: !ret (id = %d)", fv->id); return ret; } }; @@ -627,13 +627,13 @@ namespace backend iceAssert(v); - // fprintf(stderr, "add id %zu\n", fv->id); + // fprintf(stderr, "add id %d\n", fv->id); if(valueMap.find(fv->id) != valueMap.end()) - error("llvm: already have value with id %zu", fv->id); + error("llvm: already have value with id %d", fv->id); valueMap[fv->id] = v; - // printf("adding value %zu\n", fv->id); + // printf("adding value %d\n", fv->id); if(!v->getType()->isVoidTy()) v->setName(fv->getName().mangled()); @@ -915,7 +915,7 @@ namespace backend size_t i = 0; for(auto arg : ffn->getArguments()) { - DUMP_INSTR("%%%zu :: %s", arg->id, arg->getType()->str().c_str()); + DUMP_INSTR("%%%d :: %s", arg->id, arg->getType()->str().c_str()); i++; (void) arg; diff --git a/source/codegen/call.cpp b/source/codegen/call.cpp index 292415a3..d80a0c30 100644 --- a/source/codegen/call.cpp +++ b/source/codegen/call.cpp @@ -316,7 +316,7 @@ CGResult sst::FunctionCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) size_t numArgs = ft->getArgumentCount(); if(ft->isCStyleVarArg() && this->arguments.size() < numArgs) { - error(this, "need at least %zu arguments to call variadic function '%s', only have %zu", + error(this, "need at least %d arguments to call variadic function '%s', only have %d", numArgs, this->name, this->arguments.size()); } @@ -425,7 +425,7 @@ CGResult sst::ExprCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) { if((!ft->isVariadicFunc() && !ft->isCStyleVarArg()) || this->arguments.size() < ft->getArgumentCount()) { - error(this, "mismatched number of arguments; expected %zu, but %zu were given", + error(this, "mismatched number of arguments; expected %d, but %d were given", ft->getArgumentCount(), this->arguments.size()); } } diff --git a/source/codegen/literals.cpp b/source/codegen/literals.cpp index 60e28157..571914f1 100644 --- a/source/codegen/literals.cpp +++ b/source/codegen/literals.cpp @@ -200,7 +200,7 @@ CGResult sst::LiteralTuple::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(vr->getType() != ty) { - error(this->values[i], "mismatched types in tuple element %zu; expected type '%s', found type '%s'", + error(this->values[i], "mismatched types in tuple element %d; expected type '%s', found type '%s'", i, ty, vr->getType()); } diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index b63dcd44..d3b339b4 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -21,7 +21,7 @@ namespace cgn } else { - error("adding duplicate raii value (ptr = %p, type = '%s')", val, val->getType()); + error("adding duplicate raii value (ptr = %p, type = '%s')", reinterpret_cast(val), val->getType()); } } @@ -37,7 +37,7 @@ namespace cgn } else { - error("removing non-existent raii value (ptr = %p, type = '%s')", val, val->getType()); + error("removing non-existent raii value (ptr = %p, type = '%s')", reinterpret_cast(val), val->getType()); } } diff --git a/source/codegen/refcounting.cpp b/source/codegen/refcounting.cpp index 0136dd04..04be8427 100644 --- a/source/codegen/refcounting.cpp +++ b/source/codegen/refcounting.cpp @@ -13,9 +13,14 @@ namespace cgn auto list = &this->blockPointStack.back().refCountedValues; if(auto it = std::find(list->begin(), list->end(), val); it == list->end()) + { list->push_back(val); + } else - error(this->loc(), "adding duplicate refcounted value (ptr = %p, type = '%s')", val, val->getType()); + { + error(this->loc(), "adding duplicate refcounted value (ptr = %p, type = '%s')", + reinterpret_cast(val), val->getType()); + } } void CodegenState::removeRefCountedValue(fir::Value* val) @@ -23,9 +28,14 @@ namespace cgn auto list = &this->blockPointStack.back().refCountedValues; if(auto it = std::find(list->begin(), list->end(), val); it != list->end()) + { list->erase(it); + } else - error(this->loc(), "removing non-existent refcounted value (ptr = %p, type = '%s')", val, val->getType()); + { + error(this->loc(), "removing non-existent refcounted value (ptr = %p, type = '%s')", + reinterpret_cast(val), val->getType()); + } } std::vector CodegenState::getRefCountedValues() diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 2a5bccb6..893ad7cf 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -223,13 +223,13 @@ namespace fir ConstantStruct::ConstantStruct(StructType* st, std::vector members) : ConstantValue(st) { if(st->getElementCount() != members.size()) - error("mismatched structs: expected %zu fields, got %zu", st->getElementCount(), members.size()); + error("mismatched structs: expected %d fields, got %d", st->getElementCount(), members.size()); for(size_t i = 0; i < st->getElementCount(); i++) { if(st->getElementN(i) != members[i]->getType()) { - error("mismatched types in field %zu: expected '%s', got '%s'", i, st->getElementN(i), members[i]->getType()); + error("mismatched types in field %d: expected '%s', got '%s'", i, st->getElementN(i), members[i]->getType()); } } diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 96aaf9b8..339b84b4 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -759,7 +759,7 @@ namespace fir double _ = 0; if(std::modf(cfp->getValue(), &_) != 0.0) - warn("truncating constant '%Lf' in constant cast to type '%s'", cfp->getValue(), targetType); + warn("truncating constant '%f' in constant cast to type '%s'", cfp->getValue(), targetType); return ConstantInt::get(targetType, static_cast(cfp->getValue())); } @@ -924,7 +924,7 @@ namespace fir { if(args.size() != fn->getArgumentCount() && !fn->isVariadic() && !fn->isCStyleVarArg()) { - error("irbuilder: calling function '%s' with the wrong number of arguments (needs %zu, have %zu)", fn->getName().str(), + error("irbuilder: calling function '%s' with the wrong number of arguments (needs %d, have %d)", fn->getName().str(), fn->getArgumentCount(), args.size()); } @@ -974,7 +974,7 @@ namespace fir if(out[i]->getType() != target) { - error("irbuilder: mismatch in argument type (arg. %zu) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), + error("irbuilder: mismatch in argument type (arg. %d) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), fn->getArguments()[i]->getType(), out[i]->getType()); } } @@ -990,7 +990,7 @@ namespace fir } else if(args[i]->getType() != elm) { - error("irbuilder: mismatch in argument type (in variadic portion) (arg. %zu) in function '%s' (need '%s', have '%s')", + error("irbuilder: mismatch in argument type (in variadic portion) (arg. %d) in function '%s' (need '%s', have '%s')", i, fn->getName().str(), elm, args[i]->getType()); } else diff --git a/source/fir/Instruction.cpp b/source/fir/Instruction.cpp index 046e3f7f..d261bc69 100644 --- a/source/fir/Instruction.cpp +++ b/source/fir/Instruction.cpp @@ -191,7 +191,7 @@ namespace fir std::string nodes; for(auto i : phi->getValues()) - nodes += strprintf("[$%s -> %%%zu], ", i.first->getName().name, i.second->id); + nodes += strprintf("[$%s -> %%%d], ", i.first->getName().name, i.second->id); ops += nodes; } @@ -232,7 +232,7 @@ namespace fir } else if(IRBlock* ib = dcast(IRBlock, op)) { - ops += strprintf("$%zu/%s", ib->id, ib->getName().str()); + ops += strprintf("$%d/%s", ib->id, ib->getName().str()); } else { diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index 00fe127e..8d8f6998 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -7,8 +7,6 @@ #include "ir/module.h" #include "ir/irbuilder.h" -#include - namespace fir { Module::Module(std::string nm) @@ -375,7 +373,7 @@ namespace fir // do the args for(auto arg : ffn->getArguments()) { - ret += strprintf("\n arg %s (%%%zu) :: %s", arg->getName().name, arg->id, arg->getType()->str()); + ret += strprintf("\n arg %s (%%%d) :: %s", arg->getName().name, arg->id, arg->getType()->str()); } diff --git a/source/fir/Types/ArrayType.cpp b/source/fir/Types/ArrayType.cpp index 997a6004..095575ed 100644 --- a/source/fir/Types/ArrayType.cpp +++ b/source/fir/Types/ArrayType.cpp @@ -19,12 +19,12 @@ namespace fir std::string ArrayType::str() { - return strprintf("[%s: %ld]", this->arrayElementType->str(), this->getArraySize()); + return strprintf("[%s: %d]", this->arrayElementType->str(), this->getArraySize()); } std::string ArrayType::encodedStr() { - return strprintf("[%s: %ld]", this->arrayElementType->encodedStr(), this->getArraySize()); + return strprintf("[%s: %d]", this->arrayElementType->encodedStr(), this->getArraySize()); } diff --git a/source/fir/Types/Type.cpp b/source/fir/Types/Type.cpp index 8dc8d262..8a88f0f1 100644 --- a/source/fir/Types/Type.cpp +++ b/source/fir/Types/Type.cpp @@ -25,7 +25,7 @@ namespace fir void setNativeWordSizeInBits(size_t sz) { if(sz < 8 || sz > 64) - error("native word size must be >= 8 and < 64, %zu invalid", sz); + error("native word size must be >= 8 and < 64, %d invalid", sz); // we're not gonna check any further, anything becomes your problem once you change this. nativeWordSize = sz; @@ -1176,19 +1176,6 @@ namespace fir -namespace tinyformat -{ - void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty) - { - out << (ty ? ty->str() : "(null)"); - } -} - - - - - - diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 69172904..0aa90e3a 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -155,7 +155,7 @@ namespace interp static void setValueRaw(InterpState* is, interp::Value* target, void* value, size_t sz) { if(target->dataSize != sz) - error("interp: cannot set value, size mismatch (%zu vs %zu)", target->dataSize, sz); + error("interp: cannot set value, size mismatch (%d vs %d)", target->dataSize, sz); if(sz > LARGE_DATA_SIZE) memmove(target->ptr, value, sz); else memmove(&target->data[0], value, sz); @@ -400,7 +400,7 @@ namespace interp return it->second.first; else - error("interp: global value '%s' id %zu was not found", glob->getName().str(), glob->id); + error("interp: global value '%s' id %d was not found", glob->getName().str(), glob->id); } else { @@ -1200,7 +1200,7 @@ namespace interp if((!fn.func->isCStyleVarArg() && args.size() != fn.func->getArgumentCount()) || (fn.func->isCStyleVarArg() && args.size() < fn.func->getArgumentCount())) { - error("interp: mismatched argument count in call to '%s': need %zu, received %zu", + error("interp: mismatched argument count in call to '%s': need %d, received %d", fn.func->getName().str(), fn.func->getArgumentCount(), args.size()); } @@ -1243,7 +1243,7 @@ namespace interp return makeConstant(is, cnst); else - error("interp: no value with id %zu", fv->id); + error("interp: no value with id %d", fv->id); } @@ -1738,7 +1738,7 @@ namespace interp } } - if(!found) error("interp: predecessor was not listed in the PHI node (id %zu)!", phi->id); + if(!found) error("interp: predecessor was not listed in the PHI node (id %d)!", phi->id); setRet(is, inst, val); break; @@ -1771,7 +1771,7 @@ namespace interp } } - if(!target) error("interp: branch to block %zu not in current function", blk->id); + if(!target) error("interp: branch to block %d not in current function", blk->id); instrRes->targetBlk = target; return FLOW_BRANCH; @@ -1797,7 +1797,7 @@ namespace interp break; } - if(!trueblk || !falseblk) error("interp: branch to blocks %zu or %zu not in current function", trueblk->blk->id, falseblk->blk->id); + if(!trueblk || !falseblk) error("interp: branch to blocks %d or %d not in current function", trueblk->blk->id, falseblk->blk->id); if(getActualValue(cond)) @@ -1833,7 +1833,7 @@ namespace interp } } - if(!target) error("interp: no function %zu (name '%s')", fn->id, fn->getName().str()); + if(!target) error("interp: no function %d (name '%s')", fn->id, fn->getName().str()); } iceAssert(target); diff --git a/source/frontend/errors.cpp b/source/frontend/errors.cpp index 7fbf641a..5d489395 100644 --- a/source/frontend/errors.cpp +++ b/source/frontend/errors.cpp @@ -56,8 +56,8 @@ static std::string fetchContextLine(Location loc, size_t* adjust) auto lines = frontend::getFileLines(loc.fileID); if(lines.size() > loc.line) { - std::string orig = util::to_string(lines[loc.line]); std::stringstream ln; + auto orig = std::string(lines[loc.line]); // skip all leading whitespace. bool ws = true; @@ -492,6 +492,3 @@ void OverloadError::post() - - - diff --git a/source/frontend/file.cpp b/source/frontend/file.cpp index 178a46e3..11753ab1 100644 --- a/source/frontend/file.cpp +++ b/source/frontend/file.cpp @@ -20,8 +20,8 @@ namespace frontend struct FileInnards { lexer::TokenList tokens; - util::string_view fileContents; - util::FastInsertVector lines; + std::string_view fileContents; + util::FastInsertVector lines; std::vector importIndices; bool didLex = false; @@ -39,7 +39,7 @@ namespace frontend } - util::string_view fileContents; + std::string_view fileContents; { fileContents = platform::readEntireFile(fullPath); } @@ -48,10 +48,10 @@ namespace frontend // split into lines bool crlf = false; - util::FastInsertVector rawlines; + util::FastInsertVector rawlines; { - util::string_view view = fileContents; + std::string_view view = fileContents; bool first = true; while(true) @@ -61,18 +61,18 @@ namespace frontend if(first || crlf) { ln = view.find("\r\n"); - if(ln != util::string_view::npos && first) + if(ln != std::string_view::npos && first) crlf = true; } - if((!first && !crlf) || (first && !crlf && ln == util::string_view::npos)) + if((!first && !crlf) || (first && !crlf && ln == std::string_view::npos)) ln = view.find('\n'); first = false; - if(ln != util::string_view::npos) + if(ln != std::string_view::npos) { - new (rawlines.getNextSlotAndIncrement()) util::string_view(view.data(), ln + (crlf ? 2 : 1)); + new (rawlines.getNextSlotAndIncrement()) std::string_view(view.data(), ln + (crlf ? 2 : 1)); view.remove_prefix(ln + (crlf ? 2 : 1)); } else @@ -84,7 +84,7 @@ namespace frontend // account for the case when there's no trailing newline, and we still have some stuff stuck in the view. if(!view.empty()) { - new (rawlines.getNextSlotAndIncrement()) util::string_view(view.data(), view.length()); + new (rawlines.getNextSlotAndIncrement()) std::string_view(view.data(), view.length()); } } @@ -135,7 +135,7 @@ namespace frontend std::string getFileContents(const std::string& fullPath) { - return util::to_string(readFileIfNecessary(fullPath).fileContents); + return std::string(readFileIfNecessary(fullPath).fileContents); } @@ -162,7 +162,7 @@ namespace frontend } } - const util::FastInsertVector& getFileLines(size_t id) + const util::FastInsertVector& getFileLines(size_t id) { std::string fp = getFilenameFromID(id); return readFileIfNecessary(fp).lines; diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index 2cf3d6b2..9660b107 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -7,7 +7,7 @@ #include "utf8rewind/include/utf8rewind/utf8rewind.h" -using string_view = util::string_view; +using string_view = std::string_view; namespace lexer { @@ -77,7 +77,7 @@ namespace lexer } - static util::hash_map keywordMap; + static util::hash_map keywordMap; static void initKeywordMap() { if(keywordMap.size() > 0) return; @@ -629,6 +629,11 @@ namespace lexer else tok.type = TokenType::Identifier; + + // again, assume that one codepoint is 1 character wide? + // note: we convert to std::string because I believe utf8len expects null-term strings, but + // tok.text is a string_view. + unicodeLength = utf8len(std::string(tok.text).c_str()); } else if(!stream.empty() && stream[0] == '"') { @@ -700,8 +705,7 @@ namespace lexer if(i == stream.size() - 1 || stream[i] == '\n') { - error(pos, "expected closing '\"' (%zu/%zu/%zu/%c/%s/%zu)", i, stream.size(), didRead, - stream[i], util::to_string(stream), *offset); + error(pos, "expected closing '\"'"); } } @@ -780,10 +784,19 @@ namespace lexer tok.text = stream.substr(0, read); tok.type = TokenType::UnicodeSymbol; + + // assume that everything is one character wide only! + unicodeLength = 1; } else { - error(tok.loc, "unknown token '%s'", util::to_string(stream.substr(0, 10))); + // one char wide, at least. not in bytes. + auto l = tok.loc; l.len = 1; + + // get the number of bytes of the next codepoint, by seeking +1 and subtracting the pointer. + auto cplen = utf8seek(stream.data(), stream.size(), stream.data(), 1, SEEK_SET) - stream.data(); + + error(l, "unknown token '%s'", stream.substr(0, cplen)); } } @@ -813,7 +826,6 @@ namespace lexer (*offset) = 0; } - // debuglog("token %s: %d // %d\n", util::to_string(tok.text), tok.loc.col, pos.col); prevType = tok.type; prevID = tok.loc.fileID; diff --git a/source/frontend/parser/literal.cpp b/source/frontend/parser/literal.cpp index 7f04d4ae..46a650a7 100644 --- a/source/frontend/parser/literal.cpp +++ b/source/frontend/parser/literal.cpp @@ -27,7 +27,7 @@ namespace parser return util::pool(st.ploc(), t.str()); } - static std::string parseHexEscapes(const Location& loc, util::string_view sv, size_t* ofs) + static std::string parseHexEscapes(const Location& loc, std::string_view sv, size_t* ofs) { if(sv[0] == 'x') { @@ -112,7 +112,7 @@ namespace parser case 'x': // fallthrough case 'u': { size_t ofs = 0; - ss << parseHexEscapes(loc, util::string_view(str.c_str() + i, str.size() - i), &ofs); + ss << parseHexEscapes(loc, std::string_view(str.c_str() + i, str.size() - i), &ofs); i += ofs - 1; break; } diff --git a/source/include/defs.h b/source/include/defs.h index 7b015c35..cc3916d2 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -11,46 +11,51 @@ #include "utils.h" -#ifndef __has_include -#error "Please switch to a compiler that supports '__has_include'" -#endif - -/* - STRING_VIEW_TYPE documentation - - 0: normal, std::string_view - 1: experimental, std::experimental::string_view - 2: external, stx::string_view -*/ -#if __has_include() - #include - #define STRING_VIEW_TYPE 0 -#elif __has_include() - #include - #define STRING_VIEW_TYPE 1 -#else - // #error "Please switch to a compiler that supports 'string_view', or change your c++ standard version" - #include "stx/string_view.hpp" - #define STRING_VIEW_TYPE 2 -#endif - - - struct Identifier; enum class VisibilityLevel; namespace fir { struct Type; } namespace pts { struct Type; } -namespace tinyformat + + +// namespace tinyformat +// { +// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty); +// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const Identifier& id); +// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const VisibilityLevel& vl); +// } + +// #define TINYFORMAT_ERROR(x) +// #include "tinyformat/tinyformat.h" + +#include "zpr.h" +namespace zpr { - void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty); - void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const Identifier& id); - void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const VisibilityLevel& vl); -} + template + struct print_formatter) || + (std::is_pointer_v && std::is_base_of_v>) + >::type> + { + std::string print(const T& x, const format_args& args) + { + return x->str(); + } + }; -#define TINYFORMAT_ERROR(x) -#include "tinyformat/tinyformat.h" + template <> + struct print_formatter + { + std::string print(const Identifier& x, const format_args& args); + }; + + template <> + struct print_formatter + { + std::string print(const VisibilityLevel& x, const format_args& args); + }; +} @@ -58,9 +63,10 @@ namespace tinyformat [[noreturn]] void doTheExit(bool trace = true); template -[[noreturn]] inline void _error_and_exit(const char* s, Ts&&... ts) +[[noreturn]] inline void _error_and_exit(const char* fmt, Ts&&... ts) { - tinyformat::format(std::cerr, s, ts...); + // tinyformat::format(std::cerr, fmt, ts...); + fprintf(stderr, "%s", zpr::sprint(fmt, ts...).c_str()); doTheExit(); } @@ -69,7 +75,8 @@ template template std::string strprintf(const char* fmt, Ts&&... ts) { - return tinyformat::format(fmt, ts...); + // return tinyformat::format(fmt, ts...); + return zpr::sprint(fmt, ts...); } @@ -93,22 +100,6 @@ std::string strprintf(const char* fmt, Ts&&... ts) namespace util { - #ifndef STRING_VIEW_TYPE - #error "what?" - #endif - - #if STRING_VIEW_TYPE == 0 - using string_view = std::string_view; - #elif STRING_VIEW_TYPE == 1 - using string_view = std::experimental::string_view; - #elif STRING_VIEW_TYPE == 2 - using string_view = stx::string_view; - #else - #error "No string_view, or unknown type" - #endif - - - #if USE_SKA_HASHMAP using hash_map = ska::flat_hash_map; @@ -122,7 +113,6 @@ namespace util template using hash_map = std::unordered_map; #endif - } namespace fir @@ -169,7 +159,7 @@ enum class IdKind template std::string strbold(const char* fmt, Ts&&... ts) { - return std::string(COLOUR_RESET) + std::string(COLOUR_BLACK_BOLD) + tinyformat::format(fmt, ts...) + std::string(COLOUR_RESET); + return std::string(COLOUR_RESET) + std::string(COLOUR_BLACK_BOLD) + strprintf(fmt, ts...) + std::string(COLOUR_RESET); } struct Identifier @@ -644,11 +634,6 @@ struct PolyArgMapping_t namespace util { - inline std::string to_string(const string_view& sv) - { - return std::string(sv.data(), sv.length()); - } - std::string typeParamMapToString(const std::string& name, const TypeParamMap_t& map); std::string obfuscateName(const std::string& name); diff --git a/source/include/errors.h b/source/include/errors.h index 00659151..c19e6887 100644 --- a/source/include/errors.h +++ b/source/include/errors.h @@ -11,14 +11,15 @@ template inline void debuglog(const char* s, Ts&&... ts) { - auto out = tinyformat::format(s, ts...); + // auto out = tinyformat::format(s, ts...); + auto out = strprintf(s, ts...); fprintf(stderr, "%s", out.c_str()); } template inline void debuglogln(const char* s, Ts&&... ts) { - auto out = tinyformat::format(s, ts...); + auto out = strprintf(s, ts...); fprintf(stderr, "%s\n", out.c_str()); } @@ -91,7 +92,7 @@ std::string __error_gen_internal(const Location& loc, const std::string& msg, co template std::string __error_gen(const Location& loc, const char* msg, const char* type, bool, Ts&&... ts) { - return __error_gen_internal(loc, tinyformat::format(msg, ts...), type, true, false); + return __error_gen_internal(loc, strprintf(msg, ts...), type, true, false); } diff --git a/source/include/frontend.h b/source/include/frontend.h index 97bce2c2..82e62d21 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -10,6 +10,7 @@ #include "parser.h" #include "platform.h" +#include #include namespace ast @@ -102,7 +103,7 @@ namespace frontend const std::string& getFilenameFromID(size_t fileID); size_t getFileIDFromFilename(const std::string& name); lexer::TokenList& getFileTokens(const std::string& fullPath); - const util::FastInsertVector& getFileLines(size_t id); + const util::FastInsertVector& getFileLines(size_t id); const std::vector& getImportTokenLocationsForFile(const std::string& filename); std::string resolveImport(const std::string& imp, const Location& loc, const std::string& fullPath); diff --git a/source/include/ir/type.h b/source/include/ir/type.h index 7dca8229..67418577 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -484,13 +484,14 @@ namespace fir bool isMutable(); + virtual std::string str() override; + virtual std::string encodedStr() override; + virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + // protected constructor virtual ~PointerType() override { } protected: PointerType(Type* base, bool mut); - virtual std::string str() override; - virtual std::string encodedStr() override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; Type* baseType = 0; bool isPtrMutable = false; diff --git a/source/include/lexer.h b/source/include/lexer.h index f5d8020a..a942f66c 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -137,23 +137,17 @@ namespace lexer { Location loc; TokenType type = TokenType::Invalid; - util::string_view text; + std::string_view text; operator TokenType() const { return this->type; } bool operator == (const std::string& s) { return this->str() == s; } - std::string str() const { return util::to_string(this->text); } + std::string str() const { return std::string(this->text); } }; - inline void operator << (std::ostream& os, const TokenType& tt) - { - os << static_cast(tt); - } - - // using TokenList = util::FastVector; using TokenList = util::FastInsertVector; - lexer::TokenType getNextToken(const util::FastInsertVector& lines, size_t* line, size_t* offset, - const util::string_view& whole, Location& pos, Token* out, bool crlf); + lexer::TokenType getNextToken(const util::FastInsertVector& lines, size_t* line, size_t* offset, + const std::string_view& whole, Location& pos, Token* out, bool crlf); } diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 6833c113..39d50608 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -28,7 +28,7 @@ namespace parser return this->tokens[this->index + num]; else - error("lookahead %zu tokens > size %zu", num, this->tokens.size()); + error("lookahead %d tokens > size %d", num, this->tokens.size()); } void skip(size_t num) @@ -37,7 +37,7 @@ namespace parser this->index += num; else - error("skip %zu tokens > size %zu", num, this->tokens.size()); + error("skip %d tokens > size %d", num, this->tokens.size()); } void rewind(size_t num) @@ -46,13 +46,13 @@ namespace parser this->index -= num; else - error("rewind %zu tokens > index %zu", num, this->index); + error("rewind %d tokens > index %d", num, this->index); } void rewindTo(size_t ix) { if(ix >= this->tokens.size()) - error("ix %zu > size %zu", ix, this->tokens.size()); + error("ix %d > size %d", ix, this->tokens.size()); this->index = ix; } diff --git a/source/include/platform.h b/source/include/platform.h index 30fb6355..83a0217e 100644 --- a/source/include/platform.h +++ b/source/include/platform.h @@ -75,7 +75,7 @@ namespace platform size_t getFileSize(const std::string& path); bool checkFileExists(const std::string& path); - util::string_view readEntireFile(const std::string& path); + std::string_view readEntireFile(const std::string& path); std::string getFullPath(const std::string& partial); diff --git a/source/include/precompile.h b/source/include/precompile.h index 4158fe7d..86d8d910 100644 --- a/source/include/precompile.h +++ b/source/include/precompile.h @@ -10,19 +10,28 @@ #include #include #include +#include #include -#include -#include #include #include -#include -#include +#include +#include #include -#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #endif diff --git a/source/include/zpr.h b/source/include/zpr.h new file mode 100644 index 00000000..a1f73cc7 --- /dev/null +++ b/source/include/zpr.h @@ -0,0 +1,561 @@ +// zpr.h +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#pragma once +#include +#include +#include +#include + +#ifndef ENABLE_FIELD_SIZES + #define ENABLE_FIELD_SIZES 1 +#endif + +#ifndef HEX_0X_RESPECTS_UPPERCASE + #define HEX_0X_RESPECTS_UPPERCASE 1 +#endif + +namespace zpr +{ + struct format_args + { + bool zero_pad = false; + bool alternate = false; + bool prepend_plus_if_positive = false; + bool prepend_blank_if_positive = false; + + char specifier = 0; + + ssize_t width = 0; + ssize_t length = 0; + ssize_t precision = -1; + + constexpr static int LENGTH_DEFAULT = 0; + constexpr static int LENGTH_SHORT_SHORT = 1; + constexpr static int LENGTH_SHORT = 2; + constexpr static int LENGTH_LONG = 3; + constexpr static int LENGTH_LONG_LONG = 4; + constexpr static int LENGTH_LONG_DOUBLE = 5; + constexpr static int LENGTH_INTMAX_T = 6; + constexpr static int LENGTH_SIZE_T = 7; + constexpr static int LENGTH_PTRDIFF_T = 8; + }; + + template + struct print_formatter + { + template + struct has_formatter { static constexpr bool value = false; }; + + // when printing, we use print_formatter().print(...). if there is no specialisation + // for print_formatter, then we will instantiate this base class -- which causes the nice + // static_assert message. note that we must use some predicate that depends on T, so that the + // compiler can only know the value when it tries to instantiate. using static_assert(false) + // will always fail to compile. + + // we make a inner type has_formatter which is more descriptive, and since we only make this + // error when we try to instantiate the base, any specialisations don't even need to care! + static_assert(has_formatter::value, "no formatter defined for type!"); + }; + + + + + namespace _internal + { + inline format_args parseFormatArgs(const char* fmt, const char** end, bool* need_width, bool* need_prec) + { + auto ret = format_args(); + if(fmt[0] != '%') + return ret; + + fmt++; + + bool negative_width = false; + while(true) + { + switch(*fmt++) + { + case '0': ret.zero_pad = true; continue; + case '#': ret.alternate = true; continue; + case '-': negative_width = true; continue; + case '+': ret.prepend_plus_if_positive = true; continue; + case ' ': ret.prepend_blank_if_positive = true; continue; + default: fmt--; break; + } + break; + } + + if(*fmt == '*' && (fmt++, true)) + { + // note: if you use *, then the negative width is ignored! + *need_width = true; + } + else + { + while((*fmt >= '0') && (*fmt <= '9')) + ret.width = 10 * ret.width + (*fmt++ - '0'); + + if(negative_width) + ret.width *= -1; + } + + if(*fmt == '.' && (fmt++, true)) + { + if(*fmt == '*' && (fmt++, true)) + { + // int int_precision = va_arg(parameters, int); + // ret.precision = 0 <= int_precision ? (size_t) int_precision : 0; + *need_prec = true; + } + else if(*fmt == '-' && (fmt++, true)) + { + while(('0' <= *fmt) && (*fmt <= '9')) + fmt++; + } + else + { + ret.precision = 0; + while((*fmt >= '0') && (*fmt <= '9')) + ret.precision = 10 * ret.precision + (*fmt++ - '0'); + } + } + + #if ENABLE_FIELD_SIZES + + if(fmt[0] == 'h') + { + if(fmt[1] == 'h') fmt += 2, ret.length = format_args::LENGTH_SHORT_SHORT; + else fmt += 1, ret.length = format_args::LENGTH_SHORT; + } + else if(fmt[0] == 'l') + { + if(fmt[1] == 'l') fmt += 2, ret.length = format_args::LENGTH_LONG_LONG; + else fmt += 1, ret.length = format_args::LENGTH_LONG; + } + else if(fmt[0] == 'L') fmt += 1, ret.length = format_args::LENGTH_LONG_DOUBLE; + else if(fmt[0] == 't') fmt += 1, ret.length = format_args::LENGTH_PTRDIFF_T; + else if(fmt[0] == 'j') fmt += 1, ret.length = format_args::LENGTH_INTMAX_T; + else if(fmt[0] == 'z') fmt += 1, ret.length = format_args::LENGTH_SIZE_T; + + #endif + + ret.specifier = fmt[0]; + fmt++; + + *end = fmt; + return ret; + } + + + inline std::string skip(const char* fmt, const char** end) + { + std::string ret; + + top: + while(*fmt && *fmt != '%') + ret += *fmt++; + + if(*fmt && fmt[1] == '%') + { + ret += "%"; + fmt += 2; + goto top; + } + + *end = fmt; + return ret; + } + + inline std::string sprint(const char* &fmt) + { + return std::string(fmt); + } + + // we need to forward declare this. + template + std::string sprint(const char* fmt, Args&&... xs); + + + + // we need bogus ones that don't take the arguments. if we get error handling, these will throw errors. + template + std::string _consume_both_sprint(const format_args& args, const char* fmt, Args&&... xs) + { + return std::string("") + .append(sprint(fmt, xs...)); + } + + template + std::string _consume_prec_sprint(const format_args& args, const char* fmt, Args&&... xs) + { + return std::string("") + .append(sprint(fmt, xs...)); + } + + template + std::string _consume_width_sprint(const format_args& args, const char* fmt, Args&&... xs) + { + return std::string("") + .append(sprint(fmt, xs...)); + } + + template + std::string _consume_neither_sprint(const format_args& args, const char* fmt, T&& x, Args&&... xs) + { + return print_formatter>() + .print(x, args) + .append(sprint(fmt, xs...)); + } + + template >>> + std::string _consume_width_sprint(format_args args, const char* fmt, W&& width, T&& x, Args&&... xs) + { + args.width = width; + + return print_formatter>() + .print(x, args) + .append(sprint(fmt, xs...)); + } + + + template >>> + std::string _consume_prec_sprint(format_args args, const char* fmt, P&& prec, T&& x, Args&&... xs) + { + args.precision = prec; + + return print_formatter>() + .print(x, args) + .append(sprint(fmt, xs...)); + } + + template > + && std::is_integral_v>>> + std::string _consume_both_sprint(format_args args, const char* fmt, W&& width, P&& prec, T&& x, Args&&... xs) + { + args.width = width; + args.precision = prec; + + return print_formatter>() + .print(x, args) + .append(sprint(fmt, xs...)); + } + + + template + std::string sprint(const char* fmt, Args&&... xs) + { + bool need_prec = false; + bool need_width = false; + + std::string ret = skip(fmt, &fmt); + + auto args = parseFormatArgs(fmt, &fmt, &need_width, &need_prec); + + // because the if happens at runtime, all these functions need to be instantiable. that's + // why we make bogus ones that just return error strings when there aren't enough arguments. + if(need_width && need_prec) return ret.append(_consume_both_sprint(args, fmt, xs...)); + else if(need_prec) return ret.append(_consume_prec_sprint(args, fmt, xs...)); + else if(need_width) return ret.append(_consume_width_sprint(args, fmt, xs...)); + else return ret.append(_consume_neither_sprint(args, fmt, xs...)); + } + } + + + inline std::string sprint(const std::string& fmt) + { + return fmt; + } + + template + std::string sprint(const std::string& fmt, Args&&... xs) + { + return _internal::sprint(fmt.c_str(), xs...); + } + + + + + + + + + + + + + // formatters lie here + + template + struct print_formatter>> && + !std::is_same_v>> && + !std::is_same_v>> && + sizeof(T) <= sizeof(uint64_t) + ) || + (std::is_enum_v>>) + >::type> + { + std::string print(T x, format_args args) + { + int base = 10; + if(args.specifier == 'x' || args.specifier == 'X') base = 16; + else if(args.specifier == 'o') base = 8; + else if(args.specifier == 'b') base = 2; + + // handle negative values ourselves btw, due to padding + bool is_neg = false; + + if constexpr (!std::is_enum_v) + { + is_neg = (x < 0); + if(is_neg) x = -x; + } + + std::string digits; + { + // if we print base 2 we need 64 digits! + char buf[65] = {0}; + + size_t digits_len = 0; + + std::to_chars_result ret; + if constexpr (std::is_enum_v) + ret = std::to_chars(&buf[0], &buf[65], static_cast>(x), /* base: */ base); + + else + ret = std::to_chars(&buf[0], &buf[65], x, /* base: */ base); + + + if(ret.ec == std::errc()) digits_len = (ret.ptr - &buf[0]), *ret.ptr = 0; + else return ""; + + if(isupper(args.specifier)) + for(int i = 0; i < digits_len; i++) + buf[i] = toupper(buf[i]); + + digits = std::string(buf, digits_len); + } + + std::string prefix; + if(is_neg) prefix += "-"; + else if(args.prepend_plus_if_positive) prefix += "+"; + else if(args.prepend_blank_if_positive) prefix += " "; + + // prepend 0x or 0b or 0o for alternate. + ssize_t prefix_digits_length = 0; + if((base == 2 || base == 8 || base == 16) && args.alternate) + { + prefix += "0"; + #if HEX_0X_RESPECTS_UPPERCASE + prefix += args.specifier; + #else + prefix += tolower(args.specifier); + #endif + prefix_digits_length += 2; + } + + ssize_t output_length_with_precision = (args.precision == -1 + ? digits.size() + : std::max(args.precision, static_cast(digits.size())) + ); + + ssize_t digits_length = prefix_digits_length + digits.size(); + ssize_t normal_length = prefix.size() + digits.size(); + ssize_t length_with_precision = prefix.size() + output_length_with_precision; + + bool use_precision = (args.precision != -1); + bool use_zero_pad = args.zero_pad && 0 <= args.width && !use_precision; + bool use_left_pad = !use_zero_pad && 0 <= args.width; + bool use_right_pad = !use_zero_pad && args.width < 0; + + ssize_t abs_field_width = std::abs(args.width); + + std::string pre_prefix; + if(use_left_pad) + pre_prefix = std::string(std::max(ssize_t(0), abs_field_width - length_with_precision), ' '); + + std::string post_prefix; + if(use_zero_pad) + post_prefix = std::string(std::max(ssize_t(0), abs_field_width - normal_length), '0'); + + std::string prec_string; + if(use_precision) + prec_string = std::string(std::max(ssize_t(0), args.precision - digits_length), '0'); + + std::string postfix; + if(use_right_pad) + postfix = std::string(std::max(ssize_t(0), abs_field_width - length_with_precision), ' '); + + return pre_prefix + prefix + post_prefix + prec_string + digits + postfix; + } + }; + + template + struct print_formatter>>> + >::type> + { + std::string print(const T& x, const format_args& args) + { + std::string num; + { + constexpr int default_prec = 6; + + char buf[80] = { 0 }; + ssize_t num_length = 0; + + // lmao. nobody except msvc stl (and only the most recent version) implements std::to_chars + // for floating point types, even though it's in the c++17 standard. so we just cheat. + + // let printf handle the precision, but we'll handle the width and the negativity. + { + const char* fmt_str = 0; + bool longdouble = (args.length == format_args::LENGTH_LONG_DOUBLE); + + switch(args.specifier) + { + case 'E': fmt_str = (longdouble ? "%.*LE" : "%.*E"); break; + case 'e': fmt_str = (longdouble ? "%.*Le" : "%.*e"); break; + case 'F': fmt_str = (longdouble ? "%.*LF" : "%.*F"); break; + case 'f': fmt_str = (longdouble ? "%.*Lf" : "%.*f"); break; + case 'G': fmt_str = (longdouble ? "%.*LG" : "%.*G"); break; + + case 'g': [[fallthrough]]; + default: fmt_str = (longdouble ? "%.*Lg" : "%.*g"); break; + } + + num_length = snprintf(&buf[0], 80, fmt_str, + (args.precision == -1 ? default_prec : args.precision), fabs(x)); + } + + auto abs_field_width = std::abs(args.width); + + bool use_zero_pad = args.zero_pad && args.width >= 0; + bool use_left_pad = !use_zero_pad && args.width >= 0; + bool use_right_pad = !use_zero_pad && args.width < 0; + + // account for the signs, if any. + if(x < 0 || args.prepend_plus_if_positive || args.prepend_blank_if_positive) + num_length += 1; + + std::string pre_prefix; + if(use_left_pad) + pre_prefix = std::string(std::max(ssize_t(0), abs_field_width - num_length), ' '); + + std::string prefix; + if(x < 0) prefix = "-"; + else if(args.prepend_plus_if_positive) prefix = "+"; + else if(args.prepend_blank_if_positive) prefix = " "; + + std::string post_prefix; + if(use_zero_pad) + post_prefix = std::string(std::max(ssize_t(0), abs_field_width - num_length), '0'); + + std::string postfix; + if(use_right_pad) + postfix = std::string(std::max(ssize_t(0), abs_field_width - num_length), ' '); + + return pre_prefix + prefix + post_prefix + std::string(buf) + postfix; + } + + return num; + } + }; + + + template + struct print_formatter) || (std::is_same_v) || + (std::is_same_v>) || (std::is_same_v>) + >::type> + { + std::string print(const T& x, const format_args& args) + { + ssize_t string_length = 0; + ssize_t abs_field_width = std::abs(args.width); + + if constexpr (std::is_pointer_v>) + { + for(ssize_t i = 0; (args.precision != -1 ? (i < args.precision && x && x[i]) : (x && x[i])); i++) + string_length++; + } + else + { + if(args.precision >= 0) string_length = std::min(args.precision, static_cast(x.size())); + else string_length = static_cast(x.size()); + } + + std::string prefix; + if(args.width >= 0 && string_length < abs_field_width) + prefix = std::string(abs_field_width - string_length, ' '); + + std::string postfix; + if(args.width < 0 && string_length < abs_field_width) + postfix = std::string(abs_field_width - string_length, ' '); + + + if constexpr (std::is_pointer_v>) + return prefix + std::string(x, string_length) + postfix; + + else + return prefix + std::string(x, 0, string_length) + postfix; + } + }; + + template + struct print_formatter>>>) + >::type> + { + std::string print(const T& x, const format_args& args) + { + // just reuse the string printer, but with a one-char-long string. + // probably not so efficient, but idgaf for now. + return print_formatter() + .print(std::string(1, x), args); + } + }; + + template + struct print_formatter>>>) + >::type> + { + std::string print(const T& x, format_args args) + { + return print_formatter() + .print(x ? "true" : "false", args); + } + }; + + template + struct print_formatter) + >::type> + { + std::string print(const T& x, format_args args) + { + args.specifier = 'x'; + return print_formatter() + .print(reinterpret_cast(x), args); + } + }; +} + + + + + + + + + + + + diff --git a/source/main.cpp b/source/main.cpp index 5249e238..75be2680 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -18,17 +18,17 @@ struct timer { - timer() : out(nullptr) { start = std::chrono::high_resolution_clock::now(); } - explicit timer(double* t) : out(t) { start = std::chrono::high_resolution_clock::now(); } - ~timer() { if(out) *out = static_cast((std::chrono::high_resolution_clock::now() - start).count()) / 1000000.0; } - double stop() { return static_cast((std::chrono::high_resolution_clock::now() - start).count()) / 1000000.0; } + using hrc = std::chrono::high_resolution_clock; + + timer() : out(nullptr) { start = hrc::now(); } + explicit timer(double* t) : out(t) { start = hrc::now(); } + ~timer() { if(out) *out = static_cast((hrc::now() - start).count()) / 1000000.0; } + double measure() { return static_cast((hrc::now() - start).count()) / 1000000.0; } double* out = 0; - std::chrono::time_point start; + std::chrono::time_point start; }; - - static void compile(std::string in, std::string out) { auto start_time = std::chrono::high_resolution_clock::now(); @@ -43,7 +43,7 @@ static void compile(std::string in, std::string out) auto printStats = [&total](const std::string& name) { if(frontend::getPrintProfileStats()) { - debuglogln("%-9s (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", name, total.stop(), mem::getWatermark() / 1024.0, + debuglogln("%-9s (%.1f ms)\t[w: %.1fk, f: %.1fk, a: %.1fk]", name, total.measure(), mem::getWatermark() / 1024.0, mem::getDeallocatedCount() / 1024.0, mem::getAllocatedCount() / 1024.0); } }; diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 1071af0b..4a23ea5c 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -77,7 +77,7 @@ std::string util::obfuscateName(const std::string& name) } std::string util::obfuscateName(const std::string& name, size_t id) { - return strprintf("__#%s_%zu", name, id); + return strprintf("__#%s_%d", name, id); } std::string util::obfuscateName(const std::string& name, const std::string& extra) { @@ -354,27 +354,52 @@ namespace util } } - -namespace tinyformat +namespace zpr { - void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const VisibilityLevel& vl) + std::string print_formatter::print(const Identifier& x, const format_args& args) { - switch(vl) - { - case VisibilityLevel::Invalid: out << "invalid"; break; - case VisibilityLevel::Public: out << "public"; break; - case VisibilityLevel::Private: out << "private"; break; - case VisibilityLevel::Internal: out << "internal"; break; - } + return x.str(); } - void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const Identifier& id) + std::string print_formatter::print(const VisibilityLevel& x, const format_args& args) { - out << id.str(); + switch(x) + { + case VisibilityLevel::Invalid: return "invalid"; + case VisibilityLevel::Public: return "public"; + case VisibilityLevel::Private: return "private"; + case VisibilityLevel::Internal: return "internal"; + default: return "unknown"; + } } } +// namespace tinyformat +// { +// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const VisibilityLevel& vl) +// { +// switch(vl) +// { +// case VisibilityLevel::Invalid: out << "invalid"; break; +// case VisibilityLevel::Public: out << "public"; break; +// case VisibilityLevel::Private: out << "private"; break; +// case VisibilityLevel::Internal: out << "internal"; break; +// } +// } + +// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const Identifier& id) +// { +// out << id.str(); +// } + +// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty) +// { +// out << ty->str(); +// } +// } + + diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 1bd405dd..35360d35 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -183,7 +183,7 @@ namespace platform #endif } - util::string_view readEntireFile(const std::string& path) + std::string_view readEntireFile(const std::string& path) { // first, get the size of the file size_t fileLength = getFileSize(path); @@ -240,7 +240,7 @@ namespace platform iceAssert(contents); closeFile(fd); - return util::string_view(contents, fileLength); + return std::string_view(contents, fileLength); } filehandle_t openFile(const char* name, int mode, int flags) diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 697c4f21..055a7e5f 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -220,7 +220,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d auto tup = type->toTupleType(); if(n >= tup->getElementCount()) - error(dotop->right, "tuple only has %zu elements, cannot access element %zu", tup->getElementCount(), n); + error(dotop->right, "tuple only has %d elements, cannot access element %d", tup->getElementCount(), n); auto ret = util::pool(dotop->loc, tup->getElementN(n)); ret->lhs = lhs; diff --git a/source/typecheck/literals.cpp b/source/typecheck/literals.cpp index e9d82388..b1851c1e 100644 --- a/source/typecheck/literals.cpp +++ b/source/typecheck/literals.cpp @@ -108,7 +108,7 @@ TCResult ast::LitTuple::typecheck(sst::TypecheckState* fs, fir::Type* infer) auto tt = infer->toTupleType(); if(tt->getElementCount() != this->values.size()) { - error(this, "mismatched types in inferred type: have literal with %zu elements, inferred type has %zu", this->values.size(), + error(this, "mismatched types in inferred type: have literal with %d elements, inferred type has %d", this->values.size(), tt->getElementCount()); } } @@ -186,7 +186,7 @@ TCResult ast::LitArray::typecheck(sst::TypecheckState* fs, fir::Type* infer) else if(infer->isArrayType()) { if(infer->toArrayType()->getArraySize() != 0) - error(this, "array type with non-zero length %zu was inferred for empty array literal", infer->toArrayType()->getArraySize()); + error(this, "array type with non-zero length %d was inferred for empty array literal", infer->toArrayType()->getArraySize()); } else if(!(infer->isDynamicArrayType() || infer->isArraySliceType())) { diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 70c2ff29..646fe97e 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -329,8 +329,8 @@ namespace resolver } else { - error(arguments[2].loc, "string initialiser only takes 1 (from slice) or 2 (from pointer+length) arguments, found '%ld' instead", - arguments.size()); + error(arguments[2].loc, "string initialiser only takes 1 (from slice) or 2 (from pointer+length)" + " arguments, found '%d' instead", arguments.size()); } } else diff --git a/source/typecheck/resolver/resolver.cpp b/source/typecheck/resolver/resolver.cpp index 61d32c97..eae4a25c 100644 --- a/source/typecheck/resolver/resolver.cpp +++ b/source/typecheck/resolver/resolver.cpp @@ -356,7 +356,7 @@ namespace resolver } else { - auto err = SimpleError::make(callLoc, "ambiguous call to function '%s', have %zu candidates:", + auto err = SimpleError::make(callLoc, "ambiguous call to function '%s', have %d candidates:", cands[0].first->id.name, finals.size()); for(auto f : finals) diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index b45b9c22..f6ae6904 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -93,7 +93,8 @@ namespace sst util::map(f->params, [](const auto& p) -> fir::Type* { return p.type; }))) { SimpleError::make(fn->loc, "duplicate definition of function '%s' with identical signature", fn->id.name) - ->append(SimpleError::make(MsgType::Note, f->loc, "conflicting definition was here: (%p vs %p)", f, fn)) + ->append(SimpleError::make(MsgType::Note, f->loc, "conflicting definition was here: (%p vs %p)", + reinterpret_cast(f), reinterpret_cast(fn))) ->postAndQuit(); } } diff --git a/source/typecheck/type.cpp b/source/typecheck/type.cpp index 401b49da..817601f4 100644 --- a/source/typecheck/type.cpp +++ b/source/typecheck/type.cpp @@ -11,6 +11,8 @@ #include "resolver.h" #include "polymorph.h" +#include + namespace sst { fir::Type* TypecheckState::inferCorrectTypeForLiteral(fir::ConstantNumberType* type) From 6a6cdcc080a43b37fd13ded4daa6e563db6cbc13 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:27:58 +0800 Subject: [PATCH 012/129] oh no --- build/ultratiny.flx | 28 ---------------------------- source/include/defs.h | 12 +----------- source/include/precompile.h | 1 + 3 files changed, 2 insertions(+), 39 deletions(-) diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 32dc6731..3bd4dcbb 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -31,34 +31,6 @@ struct Foo : Drop, Copy, Move } } -/* - tinyformat: - 115.76 real 345.23 user 26.28 sys - 117.63 real 349.01 user 26.86 sys - 118.45 real 348.56 user 26.33 sys - 115.53 real 346.42 user 26.40 sys - 115.75 real 345.30 user 26.11 sys - -AVG: 116.62 real 346.90 user 26.39 sys - ----------------------------------------------------- - - zpr: - 101.56 real 340.92 user 22.60 sys - 102.43 real 341.59 user 22.76 sys - 99.60 real 337.48 user 22.46 sys - 100.78 real 340.38 user 22.63 sys - 102.06 real 341.57 user 22.75 sys - -AVG: 101.28 real 340.38 user 22.64 sys - ----------------------------------------------------- - - fmt: - 154.54 real 465.11 user 31.90 sys - 163.59 real 481.25 user 32.88 sys -*/ - class Bar { var data: int diff --git a/source/include/defs.h b/source/include/defs.h index cc3916d2..6dbf6d0f 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -9,6 +9,7 @@ #include #include +#include "zpr.h" #include "utils.h" @@ -19,17 +20,6 @@ namespace fir { struct Type; } namespace pts { struct Type; } -// namespace tinyformat -// { -// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty); -// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const Identifier& id); -// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const VisibilityLevel& vl); -// } - -// #define TINYFORMAT_ERROR(x) -// #include "tinyformat/tinyformat.h" - -#include "zpr.h" namespace zpr { template diff --git a/source/include/precompile.h b/source/include/precompile.h index 86d8d910..4323da31 100644 --- a/source/include/precompile.h +++ b/source/include/precompile.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include From f74a5ddb834c99e1be8d94655cd92644497afde7 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:32:11 +0800 Subject: [PATCH 013/129] oh no (2) --- source/include/zpr.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/include/zpr.h b/source/include/zpr.h index a1f73cc7..f2967a7f 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -3,6 +3,11 @@ // Licensed under the Apache License Version 2.0. #pragma once + +#include +#include +#include + #include #include #include From b7c034475470b630fccc4de66668c5e0d6b61390 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:37:31 +0800 Subject: [PATCH 014/129] oh no (3) --- source/include/zpr.h | 49 ++++++++++++++++++++++---------------------- source/main.cpp | 4 ++-- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/source/include/zpr.h b/source/include/zpr.h index f2967a7f..9948984b 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -32,9 +31,9 @@ namespace zpr char specifier = 0; - ssize_t width = 0; - ssize_t length = 0; - ssize_t precision = -1; + int64_t width = 0; + int64_t length = 0; + int64_t precision = -1; constexpr static int LENGTH_DEFAULT = 0; constexpr static int LENGTH_SHORT_SHORT = 1; @@ -341,7 +340,7 @@ namespace zpr else return ""; if(isupper(args.specifier)) - for(int i = 0; i < digits_len; i++) + for(size_t i = 0; i < digits_len; i++) buf[i] = toupper(buf[i]); digits = std::string(buf, digits_len); @@ -353,7 +352,7 @@ namespace zpr else if(args.prepend_blank_if_positive) prefix += " "; // prepend 0x or 0b or 0o for alternate. - ssize_t prefix_digits_length = 0; + int64_t prefix_digits_length = 0; if((base == 2 || base == 8 || base == 16) && args.alternate) { prefix += "0"; @@ -365,37 +364,37 @@ namespace zpr prefix_digits_length += 2; } - ssize_t output_length_with_precision = (args.precision == -1 + int64_t output_length_with_precision = (args.precision == -1 ? digits.size() - : std::max(args.precision, static_cast(digits.size())) + : std::max(args.precision, static_cast(digits.size())) ); - ssize_t digits_length = prefix_digits_length + digits.size(); - ssize_t normal_length = prefix.size() + digits.size(); - ssize_t length_with_precision = prefix.size() + output_length_with_precision; + int64_t digits_length = prefix_digits_length + digits.size(); + int64_t normal_length = prefix.size() + digits.size(); + int64_t length_with_precision = prefix.size() + output_length_with_precision; bool use_precision = (args.precision != -1); bool use_zero_pad = args.zero_pad && 0 <= args.width && !use_precision; bool use_left_pad = !use_zero_pad && 0 <= args.width; bool use_right_pad = !use_zero_pad && args.width < 0; - ssize_t abs_field_width = std::abs(args.width); + int64_t abs_field_width = std::abs(args.width); std::string pre_prefix; if(use_left_pad) - pre_prefix = std::string(std::max(ssize_t(0), abs_field_width - length_with_precision), ' '); + pre_prefix = std::string(std::max(int64_t(0), abs_field_width - length_with_precision), ' '); std::string post_prefix; if(use_zero_pad) - post_prefix = std::string(std::max(ssize_t(0), abs_field_width - normal_length), '0'); + post_prefix = std::string(std::max(int64_t(0), abs_field_width - normal_length), '0'); std::string prec_string; if(use_precision) - prec_string = std::string(std::max(ssize_t(0), args.precision - digits_length), '0'); + prec_string = std::string(std::max(int64_t(0), args.precision - digits_length), '0'); std::string postfix; if(use_right_pad) - postfix = std::string(std::max(ssize_t(0), abs_field_width - length_with_precision), ' '); + postfix = std::string(std::max(int64_t(0), abs_field_width - length_with_precision), ' '); return pre_prefix + prefix + post_prefix + prec_string + digits + postfix; } @@ -413,7 +412,7 @@ namespace zpr constexpr int default_prec = 6; char buf[80] = { 0 }; - ssize_t num_length = 0; + int64_t num_length = 0; // lmao. nobody except msvc stl (and only the most recent version) implements std::to_chars // for floating point types, even though it's in the c++17 standard. so we just cheat. @@ -451,7 +450,7 @@ namespace zpr std::string pre_prefix; if(use_left_pad) - pre_prefix = std::string(std::max(ssize_t(0), abs_field_width - num_length), ' '); + pre_prefix = std::string(std::max(int64_t(0), abs_field_width - num_length), ' '); std::string prefix; if(x < 0) prefix = "-"; @@ -460,11 +459,11 @@ namespace zpr std::string post_prefix; if(use_zero_pad) - post_prefix = std::string(std::max(ssize_t(0), abs_field_width - num_length), '0'); + post_prefix = std::string(std::max(int64_t(0), abs_field_width - num_length), '0'); std::string postfix; if(use_right_pad) - postfix = std::string(std::max(ssize_t(0), abs_field_width - num_length), ' '); + postfix = std::string(std::max(int64_t(0), abs_field_width - num_length), ' '); return pre_prefix + prefix + post_prefix + std::string(buf) + postfix; } @@ -482,18 +481,18 @@ namespace zpr { std::string print(const T& x, const format_args& args) { - ssize_t string_length = 0; - ssize_t abs_field_width = std::abs(args.width); + int64_t string_length = 0; + int64_t abs_field_width = std::abs(args.width); if constexpr (std::is_pointer_v>) { - for(ssize_t i = 0; (args.precision != -1 ? (i < args.precision && x && x[i]) : (x && x[i])); i++) + for(int64_t i = 0; (args.precision != -1 ? (i < args.precision && x && x[i]) : (x && x[i])); i++) string_length++; } else { - if(args.precision >= 0) string_length = std::min(args.precision, static_cast(x.size())); - else string_length = static_cast(x.size()); + if(args.precision >= 0) string_length = std::min(args.precision, static_cast(x.size())); + else string_length = static_cast(x.size()); } std::string prefix; diff --git a/source/main.cpp b/source/main.cpp index 75be2680..43fde325 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -8,12 +8,12 @@ #include "frontend.h" #include "ir/module.h" +#include "ir/interp.h" #include "memorypool.h" #include "allocator.h" -#include "ir/interp.h" - +#include struct timer From ed1aea2ff71042b7a7e2a3cd31b22816de6bba5c Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:41:58 +0800 Subject: [PATCH 015/129] oh no (4) --- source/include/zpr.h | 4 ++-- source/typecheck/dotop.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/include/zpr.h b/source/include/zpr.h index 9948984b..a6e01154 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -315,7 +315,7 @@ namespace zpr // handle negative values ourselves btw, due to padding bool is_neg = false; - if constexpr (!std::is_enum_v) + if constexpr (!std::is_enum_v && std::is_signed_v) { is_neg = (x < 0); if(is_neg) x = -x; @@ -341,7 +341,7 @@ namespace zpr if(isupper(args.specifier)) for(size_t i = 0; i < digits_len; i++) - buf[i] = toupper(buf[i]); + buf[i] = static_cast(toupper(buf[i])); digits = std::string(buf, digits_len); } diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 055a7e5f..74cb8b6e 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -163,7 +163,7 @@ static sst::FieldDotOp* resolveFieldNameDotOp(sst::TypecheckState* fs, sst::Expr } auto ret = dcast(sst::FieldDotOp, cur); - assert(ret); + iceAssert(ret); return ret; } From 145a3794a3f338900e56d26c7d74e3b5b27d12da Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:45:31 +0800 Subject: [PATCH 016/129] oh no (5) --- external/utf8rewind/makefile | 6 +++--- makefile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/external/utf8rewind/makefile b/external/utf8rewind/makefile index 58c50a00..feee2e1f 100644 --- a/external/utf8rewind/makefile +++ b/external/utf8rewind/makefile @@ -13,9 +13,9 @@ all: ../libutf8rewind.a @echo "# libutf8rewind" @$(AR) rcs ../libutf8rewind.a $(COBJ) -%.cc.o: %.cc - @echo "# fmt/$(notdir $<)" - @$(CXX) -std=c11 -O3 -c -Iinclude -o $@ $< +%.c.o: %.c + @echo "# utf8rewind/$(notdir $<)" + @$(CC) -std=c11 -O3 -c -Iinclude -o $@ $< diff --git a/makefile b/makefile index 924e467c..0d014b74 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ -WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wno-deprecated-declarations +WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wno-deprecated-declarations -Wno-init-list-lifetime CLANGWARNINGS := -Wno-undefined-func-template -Wno-comma -Wno-nullability-completeness -Wno-redundant-move -Wno-nested-anon-types -Wno-gnu-anonymous-struct -Wno-reserved-id-macro -Wno-extra-semi -Wno-gnu-zero-variadic-macro-arguments -Wno-shift-sign-overflow -Wno-exit-time-destructors -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-documentation-unknown-command -Wno-weak-vtables -Wno-c++98-compat -Wold-style-cast From c2ac2d7b2c64364a3a53757d46a1ecf24a8059e4 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 12:49:29 +0800 Subject: [PATCH 017/129] oh no (6) --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 0d014b74..fcf2d8a1 100644 --- a/makefile +++ b/makefile @@ -133,7 +133,7 @@ copylibs: $(FLXSRC) $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind -lfmt + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind %.cpp.o: %.cpp From f38cf097705f8623accd3a42c9b02015d8413bd0 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 13:25:36 +0800 Subject: [PATCH 018/129] oh no (7) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8c56fc98..b31c6e5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ matrix: dist: xenial sudo: false - os: osx - osx_image: xcode9.4 + osx_image: xcode11.1 cache: directories: From 6243729abf1d199a7e10a6a48d7e74c7e45c61fe Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 13:42:18 +0800 Subject: [PATCH 019/129] oh no (8) -- also, clean up unneeded dyn_cast since we have proper formatter support for fir::Type subclasses --- .travis.yml | 6 +- external/ska/flat_hash_map.hpp | 1496 -------------------------- external/tinyformat/tinyformat.h | 1046 ------------------ makefile | 24 +- source/codegen/arithmetic.cpp | 2 +- source/codegen/constructor.cpp | 2 +- source/fir/Types/ClassType.cpp | 2 +- source/fir/Types/SingleTypes.cpp | 6 +- source/include/defs.h | 21 +- source/platform/backtrace.cpp | 1 + source/typecheck/dotop.cpp | 5 +- source/typecheck/resolver/driver.cpp | 2 +- source/typecheck/traits.cpp | 2 +- 13 files changed, 27 insertions(+), 2588 deletions(-) delete mode 100644 external/ska/flat_hash_map.hpp delete mode 100644 external/tinyformat/tinyformat.h diff --git a/.travis.yml b/.travis.yml index b31c6e5c..a887a888 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ addons: - ubuntu-toolchain-r-test packages: - - g++-8 + - g++-9 - llvm-7 - llvm-7-dev - libllvm7 @@ -34,9 +34,9 @@ script: curl 'https://homebrew.bintray.com/bottles/llvm@7-7.1.0.high_sierra.bottle.tar.gz' -L -o llvm-7.1.0.tar.gz; tar -xf llvm-7.1.0.tar.gz; fi; - PATH="$(pwd)/llvm@7/7.1.0/bin:$PATH" LLVM_CONFIG=llvm-config make -j2 build; + PATH="$PATH:$(pwd)/llvm@7/7.1.0/bin" LLVM_CONFIG=llvm-config make -j2 build; else - CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION LLVM_CONFIG=llvm-config-7 make -j2 build; + CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-7 make -j2 build; fi - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx diff --git a/external/ska/flat_hash_map.hpp b/external/ska/flat_hash_map.hpp deleted file mode 100644 index a8723ee8..00000000 --- a/external/ska/flat_hash_map.hpp +++ /dev/null @@ -1,1496 +0,0 @@ -// Copyright Malte Skarupke 2017. -// Distributed under the Boost Software License, Version 1.0. -// (See http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#define SKA_NOINLINE(...) __declspec(noinline) __VA_ARGS__ -#else -#define SKA_NOINLINE(...) __VA_ARGS__ __attribute__((noinline)) -#endif - -namespace ska -{ -struct prime_number_hash_policy; -struct power_of_two_hash_policy; -struct fibonacci_hash_policy; - -namespace detailv3 -{ -template -struct functor_storage : Functor -{ - functor_storage() = default; - functor_storage(const Functor & functor) - : Functor(functor) - { - } - template - Result operator()(Args &&... args) - { - return static_cast(*this)(std::forward(args)...); - } - template - Result operator()(Args &&... args) const - { - return static_cast(*this)(std::forward(args)...); - } -}; -template -struct functor_storage -{ - typedef Result (*function_ptr)(Args...); - function_ptr function; - functor_storage(function_ptr function) - : function(function) - { - } - Result operator()(Args... args) const - { - return function(std::forward(args)...); - } - operator function_ptr &() - { - return function; - } - operator const function_ptr &() - { - return function; - } -}; -template -struct KeyOrValueHasher : functor_storage -{ - typedef functor_storage hasher_storage; - KeyOrValueHasher() = default; - KeyOrValueHasher(const hasher & hash) - : hasher_storage(hash) - { - } - size_t operator()(const key_type & key) - { - return static_cast(*this)(key); - } - size_t operator()(const key_type & key) const - { - return static_cast(*this)(key); - } - size_t operator()(const value_type & value) - { - return static_cast(*this)(value.first); - } - size_t operator()(const value_type & value) const - { - return static_cast(*this)(value.first); - } - template - size_t operator()(const std::pair & value) - { - return static_cast(*this)(value.first); - } - template - size_t operator()(const std::pair & value) const - { - return static_cast(*this)(value.first); - } -}; -template -struct KeyOrValueEquality : functor_storage -{ - typedef functor_storage equality_storage; - KeyOrValueEquality() = default; - KeyOrValueEquality(const key_equal & equality) - : equality_storage(equality) - { - } - bool operator()(const key_type & lhs, const key_type & rhs) - { - return static_cast(*this)(lhs, rhs); - } - bool operator()(const key_type & lhs, const value_type & rhs) - { - return static_cast(*this)(lhs, rhs.first); - } - bool operator()(const value_type & lhs, const key_type & rhs) - { - return static_cast(*this)(lhs.first, rhs); - } - bool operator()(const value_type & lhs, const value_type & rhs) - { - return static_cast(*this)(lhs.first, rhs.first); - } - template - bool operator()(const key_type & lhs, const std::pair & rhs) - { - return static_cast(*this)(lhs, rhs.first); - } - template - bool operator()(const std::pair & lhs, const key_type & rhs) - { - return static_cast(*this)(lhs.first, rhs); - } - template - bool operator()(const value_type & lhs, const std::pair & rhs) - { - return static_cast(*this)(lhs.first, rhs.first); - } - template - bool operator()(const std::pair & lhs, const value_type & rhs) - { - return static_cast(*this)(lhs.first, rhs.first); - } - template - bool operator()(const std::pair & lhs, const std::pair & rhs) - { - return static_cast(*this)(lhs.first, rhs.first); - } -}; -static constexpr int8_t min_lookups = 4; -template -struct sherwood_v3_entry -{ - sherwood_v3_entry() - { - } - sherwood_v3_entry(int8_t distance_from_desired) - : distance_from_desired(distance_from_desired) - { - } - ~sherwood_v3_entry() - { - } - static sherwood_v3_entry * empty_default_table() - { - static sherwood_v3_entry result[min_lookups] = { {}, {}, {}, {special_end_value} }; - return result; - } - - bool has_value() const - { - return distance_from_desired >= 0; - } - bool is_empty() const - { - return distance_from_desired < 0; - } - bool is_at_desired_position() const - { - return distance_from_desired <= 0; - } - template - void emplace(int8_t distance, Args &&... args) - { - new (std::addressof(value)) T(std::forward(args)...); - distance_from_desired = distance; - } - - void destroy_value() - { - value.~T(); - distance_from_desired = -1; - } - - int8_t distance_from_desired = -1; - static constexpr int8_t special_end_value = 0; - union { T value; }; -}; - -inline int8_t log2(size_t value) -{ - static constexpr int8_t table[64] = - { - 63, 0, 58, 1, 59, 47, 53, 2, - 60, 39, 48, 27, 54, 33, 42, 3, - 61, 51, 37, 40, 49, 18, 28, 20, - 55, 30, 34, 11, 43, 14, 22, 4, - 62, 57, 46, 52, 38, 26, 32, 41, - 50, 36, 17, 19, 29, 10, 13, 21, - 56, 45, 25, 31, 35, 16, 9, 12, - 44, 24, 15, 8, 23, 7, 6, 5 - }; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - value |= value >> 32; - return table[((value - (value >> 1)) * 0x07EDD5E59A4E28C2) >> 58]; -} - -template -struct AssignIfTrue -{ - void operator()(T & lhs, const T & rhs) - { - lhs = rhs; - } - void operator()(T & lhs, T && rhs) - { - lhs = std::move(rhs); - } -}; -template -struct AssignIfTrue -{ - void operator()(T &, const T &) - { - } - void operator()(T &, T &&) - { - } -}; - -inline size_t next_power_of_two(size_t i) -{ - --i; - i |= i >> 1; - i |= i >> 2; - i |= i >> 4; - i |= i >> 8; - i |= i >> 16; - i |= i >> 32; - ++i; - return i; -} - -template using void_t = void; - -template -struct HashPolicySelector -{ - typedef fibonacci_hash_policy type; -}; -template -struct HashPolicySelector> -{ - typedef typename T::hash_policy type; -}; - -template -class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal -{ - using Entry = detailv3::sherwood_v3_entry; - using AllocatorTraits = std::allocator_traits; - using EntryPointer = typename AllocatorTraits::pointer; - struct convertible_to_iterator; - -public: - - using value_type = T; - using size_type = size_t; - using difference_type = std::ptrdiff_t; - using hasher = ArgumentHash; - using key_equal = ArgumentEqual; - using allocator_type = EntryAlloc; - using reference = value_type &; - using const_reference = const value_type &; - using pointer = value_type *; - using const_pointer = const value_type *; - - sherwood_v3_table() - { - } - explicit sherwood_v3_table(size_type bucket_count, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) - : EntryAlloc(alloc), Hasher(hash), Equal(equal) - { - rehash(bucket_count); - } - sherwood_v3_table(size_type bucket_count, const ArgumentAlloc & alloc) - : sherwood_v3_table(bucket_count, ArgumentHash(), ArgumentEqual(), alloc) - { - } - sherwood_v3_table(size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) - : sherwood_v3_table(bucket_count, hash, ArgumentEqual(), alloc) - { - } - explicit sherwood_v3_table(const ArgumentAlloc & alloc) - : EntryAlloc(alloc) - { - } - template - sherwood_v3_table(It first, It last, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) - : sherwood_v3_table(bucket_count, hash, equal, alloc) - { - insert(first, last); - } - template - sherwood_v3_table(It first, It last, size_type bucket_count, const ArgumentAlloc & alloc) - : sherwood_v3_table(first, last, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) - { - } - template - sherwood_v3_table(It first, It last, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) - : sherwood_v3_table(first, last, bucket_count, hash, ArgumentEqual(), alloc) - { - } - sherwood_v3_table(std::initializer_list il, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) - : sherwood_v3_table(bucket_count, hash, equal, alloc) - { - if (bucket_count == 0) - rehash(il.size()); - insert(il.begin(), il.end()); - } - sherwood_v3_table(std::initializer_list il, size_type bucket_count, const ArgumentAlloc & alloc) - : sherwood_v3_table(il, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) - { - } - sherwood_v3_table(std::initializer_list il, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) - : sherwood_v3_table(il, bucket_count, hash, ArgumentEqual(), alloc) - { - } - sherwood_v3_table(const sherwood_v3_table & other) - : sherwood_v3_table(other, AllocatorTraits::select_on_container_copy_construction(other.get_allocator())) - { - } - sherwood_v3_table(const sherwood_v3_table & other, const ArgumentAlloc & alloc) - : EntryAlloc(alloc), Hasher(other), Equal(other), _max_load_factor(other._max_load_factor) - { - rehash_for_other_container(other); - try - { - insert(other.begin(), other.end()); - } - catch(...) - { - clear(); - deallocate_data(entries, num_slots_minus_one, max_lookups); - throw; - } - } - sherwood_v3_table(sherwood_v3_table && other) noexcept - : EntryAlloc(std::move(other)), Hasher(std::move(other)), Equal(std::move(other)) - { - swap_pointers(other); - } - sherwood_v3_table(sherwood_v3_table && other, const ArgumentAlloc & alloc) noexcept - : EntryAlloc(alloc), Hasher(std::move(other)), Equal(std::move(other)) - { - swap_pointers(other); - } - sherwood_v3_table & operator=(const sherwood_v3_table & other) - { - if (this == std::addressof(other)) - return *this; - - clear(); - if (AllocatorTraits::propagate_on_container_copy_assignment::value) - { - if (static_cast(*this) != static_cast(other)) - { - reset_to_empty_state(); - } - AssignIfTrue()(*this, other); - } - _max_load_factor = other._max_load_factor; - static_cast(*this) = other; - static_cast(*this) = other; - rehash_for_other_container(other); - insert(other.begin(), other.end()); - return *this; - } - sherwood_v3_table & operator=(sherwood_v3_table && other) noexcept - { - if (this == std::addressof(other)) - return *this; - else if (AllocatorTraits::propagate_on_container_move_assignment::value) - { - clear(); - reset_to_empty_state(); - AssignIfTrue()(*this, std::move(other)); - swap_pointers(other); - } - else if (static_cast(*this) == static_cast(other)) - { - swap_pointers(other); - } - else - { - clear(); - _max_load_factor = other._max_load_factor; - rehash_for_other_container(other); - for (T & elem : other) - emplace(std::move(elem)); - other.clear(); - } - static_cast(*this) = std::move(other); - static_cast(*this) = std::move(other); - return *this; - } - ~sherwood_v3_table() - { - clear(); - deallocate_data(entries, num_slots_minus_one, max_lookups); - } - - const allocator_type & get_allocator() const - { - return static_cast(*this); - } - const ArgumentEqual & key_eq() const - { - return static_cast(*this); - } - const ArgumentHash & hash_function() const - { - return static_cast(*this); - } - - template - struct templated_iterator - { - templated_iterator() = default; - templated_iterator(EntryPointer current) - : current(current) - { - } - EntryPointer current = EntryPointer(); - - using iterator_category = std::forward_iterator_tag; - using value_type = ValueType; - using difference_type = ptrdiff_t; - using pointer = ValueType *; - using reference = ValueType &; - - friend bool operator==(const templated_iterator & lhs, const templated_iterator & rhs) - { - return lhs.current == rhs.current; - } - friend bool operator!=(const templated_iterator & lhs, const templated_iterator & rhs) - { - return !(lhs == rhs); - } - - templated_iterator & operator++() - { - do - { - ++current; - } - while(current->is_empty()); - return *this; - } - templated_iterator operator++(int) - { - templated_iterator copy(*this); - ++*this; - return copy; - } - - ValueType & operator*() const - { - return current->value; - } - ValueType * operator->() const - { - return std::addressof(current->value); - } - - operator templated_iterator() const - { - return { current }; - } - }; - using iterator = templated_iterator; - using const_iterator = templated_iterator; - - iterator begin() - { - for (EntryPointer it = entries;; ++it) - { - if (it->has_value()) - return { it }; - } - } - const_iterator begin() const - { - for (EntryPointer it = entries;; ++it) - { - if (it->has_value()) - return { it }; - } - } - const_iterator cbegin() const - { - return begin(); - } - iterator end() - { - return { entries + static_cast(num_slots_minus_one + max_lookups) }; - } - const_iterator end() const - { - return { entries + static_cast(num_slots_minus_one + max_lookups) }; - } - const_iterator cend() const - { - return end(); - } - - iterator find(const FindKey & key) - { - size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); - EntryPointer it = entries + ptrdiff_t(index); - for (int8_t distance = 0; it->distance_from_desired >= distance; ++distance, ++it) - { - if (compares_equal(key, it->value)) - return { it }; - } - return end(); - } - const_iterator find(const FindKey & key) const - { - return const_cast(this)->find(key); - } - size_t count(const FindKey & key) const - { - return find(key) == end() ? 0 : 1; - } - std::pair equal_range(const FindKey & key) - { - iterator found = find(key); - if (found == end()) - return { found, found }; - else - return { found, std::next(found) }; - } - std::pair equal_range(const FindKey & key) const - { - const_iterator found = find(key); - if (found == end()) - return { found, found }; - else - return { found, std::next(found) }; - } - - template - std::pair emplace(Key && key, Args &&... args) - { - size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); - EntryPointer current_entry = entries + ptrdiff_t(index); - int8_t distance_from_desired = 0; - for (; current_entry->distance_from_desired >= distance_from_desired; ++current_entry, ++distance_from_desired) - { - if (compares_equal(key, current_entry->value)) - return { { current_entry }, false }; - } - return emplace_new_key(distance_from_desired, current_entry, std::forward(key), std::forward(args)...); - } - - std::pair insert(const value_type & value) - { - return emplace(value); - } - std::pair insert(value_type && value) - { - return emplace(std::move(value)); - } - template - iterator emplace_hint(const_iterator, Args &&... args) - { - return emplace(std::forward(args)...).first; - } - iterator insert(const_iterator, const value_type & value) - { - return emplace(value).first; - } - iterator insert(const_iterator, value_type && value) - { - return emplace(std::move(value)).first; - } - - template - void insert(It begin, It end) - { - for (; begin != end; ++begin) - { - emplace(*begin); - } - } - void insert(std::initializer_list il) - { - insert(il.begin(), il.end()); - } - - void rehash(size_t num_buckets) - { - num_buckets = std::max(num_buckets, static_cast(std::ceil(num_elements / static_cast(_max_load_factor)))); - if (num_buckets == 0) - { - reset_to_empty_state(); - return; - } - auto new_prime_index = hash_policy.next_size_over(num_buckets); - if (num_buckets == bucket_count()) - return; - int8_t new_max_lookups = compute_max_lookups(num_buckets); - EntryPointer new_buckets(AllocatorTraits::allocate(*this, num_buckets + new_max_lookups)); - EntryPointer special_end_item = new_buckets + static_cast(num_buckets + new_max_lookups - 1); - for (EntryPointer it = new_buckets; it != special_end_item; ++it) - it->distance_from_desired = -1; - special_end_item->distance_from_desired = Entry::special_end_value; - std::swap(entries, new_buckets); - std::swap(num_slots_minus_one, num_buckets); - --num_slots_minus_one; - hash_policy.commit(new_prime_index); - int8_t old_max_lookups = max_lookups; - max_lookups = new_max_lookups; - num_elements = 0; - for (EntryPointer it = new_buckets, end = it + static_cast(num_buckets + old_max_lookups); it != end; ++it) - { - if (it->has_value()) - { - emplace(std::move(it->value)); - it->destroy_value(); - } - } - deallocate_data(new_buckets, num_buckets, old_max_lookups); - } - - void reserve(size_t num_elements) - { - size_t required_buckets = num_buckets_for_reserve(num_elements); - if (required_buckets > bucket_count()) - rehash(required_buckets); - } - - // the return value is a type that can be converted to an iterator - // the reason for doing this is that it's not free to find the - // iterator pointing at the next element. if you care about the - // next iterator, turn the return value into an iterator - convertible_to_iterator erase(const_iterator to_erase) - { - EntryPointer current = to_erase.current; - current->destroy_value(); - --num_elements; - for (EntryPointer next = current + ptrdiff_t(1); !next->is_at_desired_position(); ++current, ++next) - { - current->emplace(next->distance_from_desired - 1, std::move(next->value)); - next->destroy_value(); - } - return { to_erase.current }; - } - - iterator erase(const_iterator begin_it, const_iterator end_it) - { - if (begin_it == end_it) - return { begin_it.current }; - for (EntryPointer it = begin_it.current, end = end_it.current; it != end; ++it) - { - if (it->has_value()) - { - it->destroy_value(); - --num_elements; - } - } - if (end_it == this->end()) - return this->end(); - ptrdiff_t num_to_move = std::min(static_cast(end_it.current->distance_from_desired), end_it.current - begin_it.current); - EntryPointer to_return = end_it.current - num_to_move; - for (EntryPointer it = end_it.current; !it->is_at_desired_position();) - { - EntryPointer target = it - num_to_move; - target->emplace(it->distance_from_desired - num_to_move, std::move(it->value)); - it->destroy_value(); - ++it; - num_to_move = std::min(static_cast(it->distance_from_desired), num_to_move); - } - return { to_return }; - } - - size_t erase(const FindKey & key) - { - auto found = find(key); - if (found == end()) - return 0; - else - { - erase(found); - return 1; - } - } - - void clear() - { - for (EntryPointer it = entries, end = it + static_cast(num_slots_minus_one + max_lookups); it != end; ++it) - { - if (it->has_value()) - it->destroy_value(); - } - num_elements = 0; - } - - void shrink_to_fit() - { - rehash_for_other_container(*this); - } - - void swap(sherwood_v3_table & other) - { - using std::swap; - swap_pointers(other); - swap(static_cast(*this), static_cast(other)); - swap(static_cast(*this), static_cast(other)); - if (AllocatorTraits::propagate_on_container_swap::value) - swap(static_cast(*this), static_cast(other)); - } - - size_t size() const - { - return num_elements; - } - size_t max_size() const - { - return (AllocatorTraits::max_size(*this)) / sizeof(Entry); - } - size_t bucket_count() const - { - return num_slots_minus_one ? num_slots_minus_one + 1 : 0; - } - size_type max_bucket_count() const - { - return (AllocatorTraits::max_size(*this) - min_lookups) / sizeof(Entry); - } - size_t bucket(const FindKey & key) const - { - return hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); - } - float load_factor() const - { - size_t buckets = bucket_count(); - if (buckets) - return static_cast(num_elements) / bucket_count(); - else - return 0; - } - void max_load_factor(float value) - { - _max_load_factor = value; - } - float max_load_factor() const - { - return _max_load_factor; - } - - bool empty() const - { - return num_elements == 0; - } - -private: - EntryPointer entries = Entry::empty_default_table(); - size_t num_slots_minus_one = 0; - typename HashPolicySelector::type hash_policy; - int8_t max_lookups = detailv3::min_lookups - 1; - float _max_load_factor = 0.5f; - size_t num_elements = 0; - - static int8_t compute_max_lookups(size_t num_buckets) - { - int8_t desired = detailv3::log2(num_buckets); - return std::max(detailv3::min_lookups, desired); - } - - size_t num_buckets_for_reserve(size_t num_elements) const - { - return static_cast(std::ceil(num_elements / std::min(0.5, static_cast(_max_load_factor)))); - } - void rehash_for_other_container(const sherwood_v3_table & other) - { - rehash(std::min(num_buckets_for_reserve(other.size()), other.bucket_count())); - } - - void swap_pointers(sherwood_v3_table & other) - { - using std::swap; - swap(hash_policy, other.hash_policy); - swap(entries, other.entries); - swap(num_slots_minus_one, other.num_slots_minus_one); - swap(num_elements, other.num_elements); - swap(max_lookups, other.max_lookups); - swap(_max_load_factor, other._max_load_factor); - } - - template - SKA_NOINLINE(std::pair) emplace_new_key(int8_t distance_from_desired, EntryPointer current_entry, Key && key, Args &&... args) - { - using std::swap; - if (num_slots_minus_one == 0 || distance_from_desired == max_lookups || num_elements + 1 > (num_slots_minus_one + 1) * static_cast(_max_load_factor)) - { - grow(); - return emplace(std::forward(key), std::forward(args)...); - } - else if (current_entry->is_empty()) - { - current_entry->emplace(distance_from_desired, std::forward(key), std::forward(args)...); - ++num_elements; - return { { current_entry }, true }; - } - value_type to_insert(std::forward(key), std::forward(args)...); - swap(distance_from_desired, current_entry->distance_from_desired); - swap(to_insert, current_entry->value); - iterator result = { current_entry }; - for (++distance_from_desired, ++current_entry;; ++current_entry) - { - if (current_entry->is_empty()) - { - current_entry->emplace(distance_from_desired, std::move(to_insert)); - ++num_elements; - return { result, true }; - } - else if (current_entry->distance_from_desired < distance_from_desired) - { - swap(distance_from_desired, current_entry->distance_from_desired); - swap(to_insert, current_entry->value); - ++distance_from_desired; - } - else - { - ++distance_from_desired; - if (distance_from_desired == max_lookups) - { - swap(to_insert, result.current->value); - grow(); - return emplace(std::move(to_insert)); - } - } - } - } - - void grow() - { - rehash(std::max(size_t(4), 2 * bucket_count())); - } - - void deallocate_data(EntryPointer begin, size_t num_slots_minus_one, int8_t max_lookups) - { - if (begin != Entry::empty_default_table()) - { - AllocatorTraits::deallocate(*this, begin, num_slots_minus_one + max_lookups + 1); - } - } - - void reset_to_empty_state() - { - deallocate_data(entries, num_slots_minus_one, max_lookups); - entries = Entry::empty_default_table(); - num_slots_minus_one = 0; - hash_policy.reset(); - max_lookups = detailv3::min_lookups - 1; - } - - template - size_t hash_object(const U & key) - { - return static_cast(*this)(key); - } - template - size_t hash_object(const U & key) const - { - return static_cast(*this)(key); - } - template - bool compares_equal(const L & lhs, const R & rhs) - { - return static_cast(*this)(lhs, rhs); - } - - struct convertible_to_iterator - { - EntryPointer it; - - operator iterator() - { - if (it->has_value()) - return { it }; - else - return ++iterator{it}; - } - operator const_iterator() - { - if (it->has_value()) - return { it }; - else - return ++const_iterator{it}; - } - }; - -}; -} - -struct prime_number_hash_policy -{ - static size_t mod0(size_t) { return 0llu; } - static size_t mod2(size_t hash) { return hash % 2llu; } - static size_t mod3(size_t hash) { return hash % 3llu; } - static size_t mod5(size_t hash) { return hash % 5llu; } - static size_t mod7(size_t hash) { return hash % 7llu; } - static size_t mod11(size_t hash) { return hash % 11llu; } - static size_t mod13(size_t hash) { return hash % 13llu; } - static size_t mod17(size_t hash) { return hash % 17llu; } - static size_t mod23(size_t hash) { return hash % 23llu; } - static size_t mod29(size_t hash) { return hash % 29llu; } - static size_t mod37(size_t hash) { return hash % 37llu; } - static size_t mod47(size_t hash) { return hash % 47llu; } - static size_t mod59(size_t hash) { return hash % 59llu; } - static size_t mod73(size_t hash) { return hash % 73llu; } - static size_t mod97(size_t hash) { return hash % 97llu; } - static size_t mod127(size_t hash) { return hash % 127llu; } - static size_t mod151(size_t hash) { return hash % 151llu; } - static size_t mod197(size_t hash) { return hash % 197llu; } - static size_t mod251(size_t hash) { return hash % 251llu; } - static size_t mod313(size_t hash) { return hash % 313llu; } - static size_t mod397(size_t hash) { return hash % 397llu; } - static size_t mod499(size_t hash) { return hash % 499llu; } - static size_t mod631(size_t hash) { return hash % 631llu; } - static size_t mod797(size_t hash) { return hash % 797llu; } - static size_t mod1009(size_t hash) { return hash % 1009llu; } - static size_t mod1259(size_t hash) { return hash % 1259llu; } - static size_t mod1597(size_t hash) { return hash % 1597llu; } - static size_t mod2011(size_t hash) { return hash % 2011llu; } - static size_t mod2539(size_t hash) { return hash % 2539llu; } - static size_t mod3203(size_t hash) { return hash % 3203llu; } - static size_t mod4027(size_t hash) { return hash % 4027llu; } - static size_t mod5087(size_t hash) { return hash % 5087llu; } - static size_t mod6421(size_t hash) { return hash % 6421llu; } - static size_t mod8089(size_t hash) { return hash % 8089llu; } - static size_t mod10193(size_t hash) { return hash % 10193llu; } - static size_t mod12853(size_t hash) { return hash % 12853llu; } - static size_t mod16193(size_t hash) { return hash % 16193llu; } - static size_t mod20399(size_t hash) { return hash % 20399llu; } - static size_t mod25717(size_t hash) { return hash % 25717llu; } - static size_t mod32401(size_t hash) { return hash % 32401llu; } - static size_t mod40823(size_t hash) { return hash % 40823llu; } - static size_t mod51437(size_t hash) { return hash % 51437llu; } - static size_t mod64811(size_t hash) { return hash % 64811llu; } - static size_t mod81649(size_t hash) { return hash % 81649llu; } - static size_t mod102877(size_t hash) { return hash % 102877llu; } - static size_t mod129607(size_t hash) { return hash % 129607llu; } - static size_t mod163307(size_t hash) { return hash % 163307llu; } - static size_t mod205759(size_t hash) { return hash % 205759llu; } - static size_t mod259229(size_t hash) { return hash % 259229llu; } - static size_t mod326617(size_t hash) { return hash % 326617llu; } - static size_t mod411527(size_t hash) { return hash % 411527llu; } - static size_t mod518509(size_t hash) { return hash % 518509llu; } - static size_t mod653267(size_t hash) { return hash % 653267llu; } - static size_t mod823117(size_t hash) { return hash % 823117llu; } - static size_t mod1037059(size_t hash) { return hash % 1037059llu; } - static size_t mod1306601(size_t hash) { return hash % 1306601llu; } - static size_t mod1646237(size_t hash) { return hash % 1646237llu; } - static size_t mod2074129(size_t hash) { return hash % 2074129llu; } - static size_t mod2613229(size_t hash) { return hash % 2613229llu; } - static size_t mod3292489(size_t hash) { return hash % 3292489llu; } - static size_t mod4148279(size_t hash) { return hash % 4148279llu; } - static size_t mod5226491(size_t hash) { return hash % 5226491llu; } - static size_t mod6584983(size_t hash) { return hash % 6584983llu; } - static size_t mod8296553(size_t hash) { return hash % 8296553llu; } - static size_t mod10453007(size_t hash) { return hash % 10453007llu; } - static size_t mod13169977(size_t hash) { return hash % 13169977llu; } - static size_t mod16593127(size_t hash) { return hash % 16593127llu; } - static size_t mod20906033(size_t hash) { return hash % 20906033llu; } - static size_t mod26339969(size_t hash) { return hash % 26339969llu; } - static size_t mod33186281(size_t hash) { return hash % 33186281llu; } - static size_t mod41812097(size_t hash) { return hash % 41812097llu; } - static size_t mod52679969(size_t hash) { return hash % 52679969llu; } - static size_t mod66372617(size_t hash) { return hash % 66372617llu; } - static size_t mod83624237(size_t hash) { return hash % 83624237llu; } - static size_t mod105359939(size_t hash) { return hash % 105359939llu; } - static size_t mod132745199(size_t hash) { return hash % 132745199llu; } - static size_t mod167248483(size_t hash) { return hash % 167248483llu; } - static size_t mod210719881(size_t hash) { return hash % 210719881llu; } - static size_t mod265490441(size_t hash) { return hash % 265490441llu; } - static size_t mod334496971(size_t hash) { return hash % 334496971llu; } - static size_t mod421439783(size_t hash) { return hash % 421439783llu; } - static size_t mod530980861(size_t hash) { return hash % 530980861llu; } - static size_t mod668993977(size_t hash) { return hash % 668993977llu; } - static size_t mod842879579(size_t hash) { return hash % 842879579llu; } - static size_t mod1061961721(size_t hash) { return hash % 1061961721llu; } - static size_t mod1337987929(size_t hash) { return hash % 1337987929llu; } - static size_t mod1685759167(size_t hash) { return hash % 1685759167llu; } - static size_t mod2123923447(size_t hash) { return hash % 2123923447llu; } - static size_t mod2675975881(size_t hash) { return hash % 2675975881llu; } - static size_t mod3371518343(size_t hash) { return hash % 3371518343llu; } - static size_t mod4247846927(size_t hash) { return hash % 4247846927llu; } - static size_t mod5351951779(size_t hash) { return hash % 5351951779llu; } - static size_t mod6743036717(size_t hash) { return hash % 6743036717llu; } - static size_t mod8495693897(size_t hash) { return hash % 8495693897llu; } - static size_t mod10703903591(size_t hash) { return hash % 10703903591llu; } - static size_t mod13486073473(size_t hash) { return hash % 13486073473llu; } - static size_t mod16991387857(size_t hash) { return hash % 16991387857llu; } - static size_t mod21407807219(size_t hash) { return hash % 21407807219llu; } - static size_t mod26972146961(size_t hash) { return hash % 26972146961llu; } - static size_t mod33982775741(size_t hash) { return hash % 33982775741llu; } - static size_t mod42815614441(size_t hash) { return hash % 42815614441llu; } - static size_t mod53944293929(size_t hash) { return hash % 53944293929llu; } - static size_t mod67965551447(size_t hash) { return hash % 67965551447llu; } - static size_t mod85631228929(size_t hash) { return hash % 85631228929llu; } - static size_t mod107888587883(size_t hash) { return hash % 107888587883llu; } - static size_t mod135931102921(size_t hash) { return hash % 135931102921llu; } - static size_t mod171262457903(size_t hash) { return hash % 171262457903llu; } - static size_t mod215777175787(size_t hash) { return hash % 215777175787llu; } - static size_t mod271862205833(size_t hash) { return hash % 271862205833llu; } - static size_t mod342524915839(size_t hash) { return hash % 342524915839llu; } - static size_t mod431554351609(size_t hash) { return hash % 431554351609llu; } - static size_t mod543724411781(size_t hash) { return hash % 543724411781llu; } - static size_t mod685049831731(size_t hash) { return hash % 685049831731llu; } - static size_t mod863108703229(size_t hash) { return hash % 863108703229llu; } - static size_t mod1087448823553(size_t hash) { return hash % 1087448823553llu; } - static size_t mod1370099663459(size_t hash) { return hash % 1370099663459llu; } - static size_t mod1726217406467(size_t hash) { return hash % 1726217406467llu; } - static size_t mod2174897647073(size_t hash) { return hash % 2174897647073llu; } - static size_t mod2740199326961(size_t hash) { return hash % 2740199326961llu; } - static size_t mod3452434812973(size_t hash) { return hash % 3452434812973llu; } - static size_t mod4349795294267(size_t hash) { return hash % 4349795294267llu; } - static size_t mod5480398654009(size_t hash) { return hash % 5480398654009llu; } - static size_t mod6904869625999(size_t hash) { return hash % 6904869625999llu; } - static size_t mod8699590588571(size_t hash) { return hash % 8699590588571llu; } - static size_t mod10960797308051(size_t hash) { return hash % 10960797308051llu; } - static size_t mod13809739252051(size_t hash) { return hash % 13809739252051llu; } - static size_t mod17399181177241(size_t hash) { return hash % 17399181177241llu; } - static size_t mod21921594616111(size_t hash) { return hash % 21921594616111llu; } - static size_t mod27619478504183(size_t hash) { return hash % 27619478504183llu; } - static size_t mod34798362354533(size_t hash) { return hash % 34798362354533llu; } - static size_t mod43843189232363(size_t hash) { return hash % 43843189232363llu; } - static size_t mod55238957008387(size_t hash) { return hash % 55238957008387llu; } - static size_t mod69596724709081(size_t hash) { return hash % 69596724709081llu; } - static size_t mod87686378464759(size_t hash) { return hash % 87686378464759llu; } - static size_t mod110477914016779(size_t hash) { return hash % 110477914016779llu; } - static size_t mod139193449418173(size_t hash) { return hash % 139193449418173llu; } - static size_t mod175372756929481(size_t hash) { return hash % 175372756929481llu; } - static size_t mod220955828033581(size_t hash) { return hash % 220955828033581llu; } - static size_t mod278386898836457(size_t hash) { return hash % 278386898836457llu; } - static size_t mod350745513859007(size_t hash) { return hash % 350745513859007llu; } - static size_t mod441911656067171(size_t hash) { return hash % 441911656067171llu; } - static size_t mod556773797672909(size_t hash) { return hash % 556773797672909llu; } - static size_t mod701491027718027(size_t hash) { return hash % 701491027718027llu; } - static size_t mod883823312134381(size_t hash) { return hash % 883823312134381llu; } - static size_t mod1113547595345903(size_t hash) { return hash % 1113547595345903llu; } - static size_t mod1402982055436147(size_t hash) { return hash % 1402982055436147llu; } - static size_t mod1767646624268779(size_t hash) { return hash % 1767646624268779llu; } - static size_t mod2227095190691797(size_t hash) { return hash % 2227095190691797llu; } - static size_t mod2805964110872297(size_t hash) { return hash % 2805964110872297llu; } - static size_t mod3535293248537579(size_t hash) { return hash % 3535293248537579llu; } - static size_t mod4454190381383713(size_t hash) { return hash % 4454190381383713llu; } - static size_t mod5611928221744609(size_t hash) { return hash % 5611928221744609llu; } - static size_t mod7070586497075177(size_t hash) { return hash % 7070586497075177llu; } - static size_t mod8908380762767489(size_t hash) { return hash % 8908380762767489llu; } - static size_t mod11223856443489329(size_t hash) { return hash % 11223856443489329llu; } - static size_t mod14141172994150357(size_t hash) { return hash % 14141172994150357llu; } - static size_t mod17816761525534927(size_t hash) { return hash % 17816761525534927llu; } - static size_t mod22447712886978529(size_t hash) { return hash % 22447712886978529llu; } - static size_t mod28282345988300791(size_t hash) { return hash % 28282345988300791llu; } - static size_t mod35633523051069991(size_t hash) { return hash % 35633523051069991llu; } - static size_t mod44895425773957261(size_t hash) { return hash % 44895425773957261llu; } - static size_t mod56564691976601587(size_t hash) { return hash % 56564691976601587llu; } - static size_t mod71267046102139967(size_t hash) { return hash % 71267046102139967llu; } - static size_t mod89790851547914507(size_t hash) { return hash % 89790851547914507llu; } - static size_t mod113129383953203213(size_t hash) { return hash % 113129383953203213llu; } - static size_t mod142534092204280003(size_t hash) { return hash % 142534092204280003llu; } - static size_t mod179581703095829107(size_t hash) { return hash % 179581703095829107llu; } - static size_t mod226258767906406483(size_t hash) { return hash % 226258767906406483llu; } - static size_t mod285068184408560057(size_t hash) { return hash % 285068184408560057llu; } - static size_t mod359163406191658253(size_t hash) { return hash % 359163406191658253llu; } - static size_t mod452517535812813007(size_t hash) { return hash % 452517535812813007llu; } - static size_t mod570136368817120201(size_t hash) { return hash % 570136368817120201llu; } - static size_t mod718326812383316683(size_t hash) { return hash % 718326812383316683llu; } - static size_t mod905035071625626043(size_t hash) { return hash % 905035071625626043llu; } - static size_t mod1140272737634240411(size_t hash) { return hash % 1140272737634240411llu; } - static size_t mod1436653624766633509(size_t hash) { return hash % 1436653624766633509llu; } - static size_t mod1810070143251252131(size_t hash) { return hash % 1810070143251252131llu; } - static size_t mod2280545475268481167(size_t hash) { return hash % 2280545475268481167llu; } - static size_t mod2873307249533267101(size_t hash) { return hash % 2873307249533267101llu; } - static size_t mod3620140286502504283(size_t hash) { return hash % 3620140286502504283llu; } - static size_t mod4561090950536962147(size_t hash) { return hash % 4561090950536962147llu; } - static size_t mod5746614499066534157(size_t hash) { return hash % 5746614499066534157llu; } - static size_t mod7240280573005008577(size_t hash) { return hash % 7240280573005008577llu; } - static size_t mod9122181901073924329(size_t hash) { return hash % 9122181901073924329llu; } - static size_t mod11493228998133068689(size_t hash) { return hash % 11493228998133068689llu; } - static size_t mod14480561146010017169(size_t hash) { return hash % 14480561146010017169llu; } - static size_t mod18446744073709551557(size_t hash) { return hash % 18446744073709551557llu; } - - using mod_function = size_t (*)(size_t); - - mod_function next_size_over(size_t & size) const - { - // prime numbers generated by the following method: - // 1. start with a prime p = 2 - // 2. go to wolfram alpha and get p = NextPrime(2 * p) - // 3. repeat 2. until you overflow 64 bits - // you now have large gaps which you would hit if somebody called reserve() with an unlucky number. - // 4. to fill the gaps for every prime p go to wolfram alpha and get ClosestPrime(p * 2^(1/3)) and ClosestPrime(p * 2^(2/3)) and put those in the gaps - // 5. get PrevPrime(2^64) and put it at the end - static constexpr const size_t prime_list[] = - { - 2llu, 3llu, 5llu, 7llu, 11llu, 13llu, 17llu, 23llu, 29llu, 37llu, 47llu, - 59llu, 73llu, 97llu, 127llu, 151llu, 197llu, 251llu, 313llu, 397llu, - 499llu, 631llu, 797llu, 1009llu, 1259llu, 1597llu, 2011llu, 2539llu, - 3203llu, 4027llu, 5087llu, 6421llu, 8089llu, 10193llu, 12853llu, 16193llu, - 20399llu, 25717llu, 32401llu, 40823llu, 51437llu, 64811llu, 81649llu, - 102877llu, 129607llu, 163307llu, 205759llu, 259229llu, 326617llu, - 411527llu, 518509llu, 653267llu, 823117llu, 1037059llu, 1306601llu, - 1646237llu, 2074129llu, 2613229llu, 3292489llu, 4148279llu, 5226491llu, - 6584983llu, 8296553llu, 10453007llu, 13169977llu, 16593127llu, 20906033llu, - 26339969llu, 33186281llu, 41812097llu, 52679969llu, 66372617llu, - 83624237llu, 105359939llu, 132745199llu, 167248483llu, 210719881llu, - 265490441llu, 334496971llu, 421439783llu, 530980861llu, 668993977llu, - 842879579llu, 1061961721llu, 1337987929llu, 1685759167llu, 2123923447llu, - 2675975881llu, 3371518343llu, 4247846927llu, 5351951779llu, 6743036717llu, - 8495693897llu, 10703903591llu, 13486073473llu, 16991387857llu, - 21407807219llu, 26972146961llu, 33982775741llu, 42815614441llu, - 53944293929llu, 67965551447llu, 85631228929llu, 107888587883llu, - 135931102921llu, 171262457903llu, 215777175787llu, 271862205833llu, - 342524915839llu, 431554351609llu, 543724411781llu, 685049831731llu, - 863108703229llu, 1087448823553llu, 1370099663459llu, 1726217406467llu, - 2174897647073llu, 2740199326961llu, 3452434812973llu, 4349795294267llu, - 5480398654009llu, 6904869625999llu, 8699590588571llu, 10960797308051llu, - 13809739252051llu, 17399181177241llu, 21921594616111llu, 27619478504183llu, - 34798362354533llu, 43843189232363llu, 55238957008387llu, 69596724709081llu, - 87686378464759llu, 110477914016779llu, 139193449418173llu, - 175372756929481llu, 220955828033581llu, 278386898836457llu, - 350745513859007llu, 441911656067171llu, 556773797672909llu, - 701491027718027llu, 883823312134381llu, 1113547595345903llu, - 1402982055436147llu, 1767646624268779llu, 2227095190691797llu, - 2805964110872297llu, 3535293248537579llu, 4454190381383713llu, - 5611928221744609llu, 7070586497075177llu, 8908380762767489llu, - 11223856443489329llu, 14141172994150357llu, 17816761525534927llu, - 22447712886978529llu, 28282345988300791llu, 35633523051069991llu, - 44895425773957261llu, 56564691976601587llu, 71267046102139967llu, - 89790851547914507llu, 113129383953203213llu, 142534092204280003llu, - 179581703095829107llu, 226258767906406483llu, 285068184408560057llu, - 359163406191658253llu, 452517535812813007llu, 570136368817120201llu, - 718326812383316683llu, 905035071625626043llu, 1140272737634240411llu, - 1436653624766633509llu, 1810070143251252131llu, 2280545475268481167llu, - 2873307249533267101llu, 3620140286502504283llu, 4561090950536962147llu, - 5746614499066534157llu, 7240280573005008577llu, 9122181901073924329llu, - 11493228998133068689llu, 14480561146010017169llu, 18446744073709551557llu - }; - static constexpr size_t (* const mod_functions[])(size_t) = - { - &mod0, &mod2, &mod3, &mod5, &mod7, &mod11, &mod13, &mod17, &mod23, &mod29, &mod37, - &mod47, &mod59, &mod73, &mod97, &mod127, &mod151, &mod197, &mod251, &mod313, &mod397, - &mod499, &mod631, &mod797, &mod1009, &mod1259, &mod1597, &mod2011, &mod2539, &mod3203, - &mod4027, &mod5087, &mod6421, &mod8089, &mod10193, &mod12853, &mod16193, &mod20399, - &mod25717, &mod32401, &mod40823, &mod51437, &mod64811, &mod81649, &mod102877, - &mod129607, &mod163307, &mod205759, &mod259229, &mod326617, &mod411527, &mod518509, - &mod653267, &mod823117, &mod1037059, &mod1306601, &mod1646237, &mod2074129, - &mod2613229, &mod3292489, &mod4148279, &mod5226491, &mod6584983, &mod8296553, - &mod10453007, &mod13169977, &mod16593127, &mod20906033, &mod26339969, &mod33186281, - &mod41812097, &mod52679969, &mod66372617, &mod83624237, &mod105359939, &mod132745199, - &mod167248483, &mod210719881, &mod265490441, &mod334496971, &mod421439783, - &mod530980861, &mod668993977, &mod842879579, &mod1061961721, &mod1337987929, - &mod1685759167, &mod2123923447, &mod2675975881, &mod3371518343, &mod4247846927, - &mod5351951779, &mod6743036717, &mod8495693897, &mod10703903591, &mod13486073473, - &mod16991387857, &mod21407807219, &mod26972146961, &mod33982775741, &mod42815614441, - &mod53944293929, &mod67965551447, &mod85631228929, &mod107888587883, &mod135931102921, - &mod171262457903, &mod215777175787, &mod271862205833, &mod342524915839, - &mod431554351609, &mod543724411781, &mod685049831731, &mod863108703229, - &mod1087448823553, &mod1370099663459, &mod1726217406467, &mod2174897647073, - &mod2740199326961, &mod3452434812973, &mod4349795294267, &mod5480398654009, - &mod6904869625999, &mod8699590588571, &mod10960797308051, &mod13809739252051, - &mod17399181177241, &mod21921594616111, &mod27619478504183, &mod34798362354533, - &mod43843189232363, &mod55238957008387, &mod69596724709081, &mod87686378464759, - &mod110477914016779, &mod139193449418173, &mod175372756929481, &mod220955828033581, - &mod278386898836457, &mod350745513859007, &mod441911656067171, &mod556773797672909, - &mod701491027718027, &mod883823312134381, &mod1113547595345903, &mod1402982055436147, - &mod1767646624268779, &mod2227095190691797, &mod2805964110872297, &mod3535293248537579, - &mod4454190381383713, &mod5611928221744609, &mod7070586497075177, &mod8908380762767489, - &mod11223856443489329, &mod14141172994150357, &mod17816761525534927, - &mod22447712886978529, &mod28282345988300791, &mod35633523051069991, - &mod44895425773957261, &mod56564691976601587, &mod71267046102139967, - &mod89790851547914507, &mod113129383953203213, &mod142534092204280003, - &mod179581703095829107, &mod226258767906406483, &mod285068184408560057, - &mod359163406191658253, &mod452517535812813007, &mod570136368817120201, - &mod718326812383316683, &mod905035071625626043, &mod1140272737634240411, - &mod1436653624766633509, &mod1810070143251252131, &mod2280545475268481167, - &mod2873307249533267101, &mod3620140286502504283, &mod4561090950536962147, - &mod5746614499066534157, &mod7240280573005008577, &mod9122181901073924329, - &mod11493228998133068689, &mod14480561146010017169, &mod18446744073709551557 - }; - const size_t * found = std::lower_bound(std::begin(prime_list), std::end(prime_list) - 1, size); - size = *found; - return mod_functions[1 + found - prime_list]; - } - void commit(mod_function new_mod_function) - { - current_mod_function = new_mod_function; - } - void reset() - { - current_mod_function = &mod0; - } - - size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const - { - return current_mod_function(hash); - } - size_t keep_in_range(size_t index, size_t num_slots_minus_one) const - { - return index > num_slots_minus_one ? current_mod_function(index) : index; - } - -private: - mod_function current_mod_function = &mod0; -}; - -struct power_of_two_hash_policy -{ - size_t index_for_hash(size_t hash, size_t num_slots_minus_one) const - { - return hash & num_slots_minus_one; - } - size_t keep_in_range(size_t index, size_t num_slots_minus_one) const - { - return index_for_hash(index, num_slots_minus_one); - } - int8_t next_size_over(size_t & size) const - { - size = detailv3::next_power_of_two(size); - return 0; - } - void commit(int8_t) - { - } - void reset() - { - } - -}; - -struct fibonacci_hash_policy -{ - size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const - { - return (11400714819323198485ull * hash) >> shift; - } - size_t keep_in_range(size_t index, size_t num_slots_minus_one) const - { - return index & num_slots_minus_one; - } - - int8_t next_size_over(size_t & size) const - { - size = std::max(size_t(2), detailv3::next_power_of_two(size)); - return 64 - detailv3::log2(size); - } - void commit(int8_t shift) - { - this->shift = shift; - } - void reset() - { - shift = 63; - } - -private: - int8_t shift = 63; -}; - -template, typename E = std::equal_to, typename A = std::allocator > > -class flat_hash_map - : public detailv3::sherwood_v3_table - < - std::pair, - K, - H, - detailv3::KeyOrValueHasher, H>, - E, - detailv3::KeyOrValueEquality, E>, - A, - typename std::allocator_traits::template rebind_alloc>> - > -{ - using Table = detailv3::sherwood_v3_table - < - std::pair, - K, - H, - detailv3::KeyOrValueHasher, H>, - E, - detailv3::KeyOrValueEquality, E>, - A, - typename std::allocator_traits::template rebind_alloc>> - >; -public: - - using key_type = K; - using mapped_type = V; - - using Table::Table; - flat_hash_map() - { - } - - inline V & operator[](const K & key) - { - return emplace(key, convertible_to_value()).first->second; - } - inline V & operator[](K && key) - { - return emplace(std::move(key), convertible_to_value()).first->second; - } - V & at(const K & key) - { - auto found = this->find(key); - if (found == this->end()) - throw std::out_of_range("Argument passed to at() was not in the map."); - return found->second; - } - const V & at(const K & key) const - { - auto found = this->find(key); - if (found == this->end()) - throw std::out_of_range("Argument passed to at() was not in the map."); - return found->second; - } - - using Table::emplace; - std::pair emplace() - { - return emplace(key_type(), convertible_to_value()); - } - template - std::pair insert_or_assign(const key_type & key, M && m) - { - auto emplace_result = emplace(key, std::forward(m)); - if (!emplace_result.second) - emplace_result.first->second = std::forward(m); - return emplace_result; - } - template - std::pair insert_or_assign(key_type && key, M && m) - { - auto emplace_result = emplace(std::move(key), std::forward(m)); - if (!emplace_result.second) - emplace_result.first->second = std::forward(m); - return emplace_result; - } - template - typename Table::iterator insert_or_assign(typename Table::const_iterator, const key_type & key, M && m) - { - return insert_or_assign(key, std::forward(m)).first; - } - template - typename Table::iterator insert_or_assign(typename Table::const_iterator, key_type && key, M && m) - { - return insert_or_assign(std::move(key), std::forward(m)).first; - } - - friend bool operator==(const flat_hash_map & lhs, const flat_hash_map & rhs) - { - if (lhs.size() != rhs.size()) - return false; - for (const typename Table::value_type & value : lhs) - { - auto found = rhs.find(value.first); - if (found == rhs.end()) - return false; - else if (value.second != found->second) - return false; - } - return true; - } - friend bool operator!=(const flat_hash_map & lhs, const flat_hash_map & rhs) - { - return !(lhs == rhs); - } - -private: - struct convertible_to_value - { - operator V() const - { - return V(); - } - }; -}; - -template, typename E = std::equal_to, typename A = std::allocator > -class flat_hash_set - : public detailv3::sherwood_v3_table - < - T, - T, - H, - detailv3::functor_storage, - E, - detailv3::functor_storage, - A, - typename std::allocator_traits::template rebind_alloc> - > -{ - using Table = detailv3::sherwood_v3_table - < - T, - T, - H, - detailv3::functor_storage, - E, - detailv3::functor_storage, - A, - typename std::allocator_traits::template rebind_alloc> - >; -public: - - using key_type = T; - - using Table::Table; - flat_hash_set() - { - } - - template - std::pair emplace(Args &&... args) - { - return Table::emplace(T(std::forward(args)...)); - } - std::pair emplace(const key_type & arg) - { - return Table::emplace(arg); - } - std::pair emplace(key_type & arg) - { - return Table::emplace(arg); - } - std::pair emplace(const key_type && arg) - { - return Table::emplace(std::move(arg)); - } - std::pair emplace(key_type && arg) - { - return Table::emplace(std::move(arg)); - } - - friend bool operator==(const flat_hash_set & lhs, const flat_hash_set & rhs) - { - if (lhs.size() != rhs.size()) - return false; - for (const T & value : lhs) - { - if (rhs.find(value) == rhs.end()) - return false; - } - return true; - } - friend bool operator!=(const flat_hash_set & lhs, const flat_hash_set & rhs) - { - return !(lhs == rhs); - } -}; - - -template -struct power_of_two_std_hash : std::hash -{ - typedef ska::power_of_two_hash_policy hash_policy; -}; - -} // end namespace ska diff --git a/external/tinyformat/tinyformat.h b/external/tinyformat/tinyformat.h deleted file mode 100644 index 7c22ca68..00000000 --- a/external/tinyformat/tinyformat.h +++ /dev/null @@ -1,1046 +0,0 @@ -// tinyformat.h -// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] -// -// Boost Software License - Version 1.0 -// -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//------------------------------------------------------------------------------ -// Tinyformat: A minimal type safe printf replacement -// -// tinyformat.h is a type safe printf replacement library in a single C++ -// header file. Design goals include: -// -// * Type safety and extensibility for user defined types. -// * C99 printf() compatibility, to the extent possible using std::ostream -// * Simplicity and minimalism. A single header file to include and distribute -// with your projects. -// * Augment rather than replace the standard stream formatting mechanism -// * C++98 support, with optional C++11 niceties -// -// -// Main interface example usage -// ---------------------------- -// -// To print a date to std::cout: -// -// std::string weekday = "Wednesday"; -// const char* month = "July"; -// size_t day = 27; -// long hour = 14; -// int min = 44; -// -// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); -// -// The strange types here emphasize the type safety of the interface; it is -// possible to print a std::string using the "%s" conversion, and a -// size_t using the "%d" conversion. A similar result could be achieved -// using either of the tfm::format() functions. One prints on a user provided -// stream: -// -// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", -// weekday, month, day, hour, min); -// -// The other returns a std::string: -// -// std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", -// weekday, month, day, hour, min); -// std::cout << date; -// -// These are the three primary interface functions. There is also a -// convenience function printfln() which appends a newline to the usual result -// of printf() for super simple logging. -// -// -// User defined format functions -// ----------------------------- -// -// Simulating variadic templates in C++98 is pretty painful since it requires -// writing out the same function for each desired number of arguments. To make -// this bearable tinyformat comes with a set of macros which are used -// internally to generate the API, but which may also be used in user code. -// -// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and -// TINYFORMAT_PASSARGS(n) will generate a list of n argument types, -// type/name pairs and argument names respectively when called with an integer -// n between 1 and 16. We can use these to define a macro which generates the -// desired user defined function with n arguments. To generate all 16 user -// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an -// example, see the implementation of printf() at the end of the source file. -// -// Sometimes it's useful to be able to pass a list of format arguments through -// to a non-template function. The FormatList class is provided as a way to do -// this by storing the argument list in a type-opaque way. Continuing the -// example from above, we construct a FormatList using makeFormatList(): -// -// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min); -// -// The format list can now be passed into any non-template function and used -// via a call to the vformat() function: -// -// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList); -// -// -// Additional API information -// -------------------------- -// -// Error handling: Define TINYFORMAT_ERROR to customize the error handling for -// format strings which are unsupported or have the wrong number of format -// specifiers (calls assert() by default). -// -// User defined types: Uses operator<< for user defined types by default. -// Overload formatValue() for more control. - - -#ifndef TINYFORMAT_H_INCLUDED -#define TINYFORMAT_H_INCLUDED - -namespace tinyformat {} -//------------------------------------------------------------------------------ -// Config section. Customize to your liking! - -// Namespace alias to encourage brevity -namespace tfm = tinyformat; - -// Error handling; calls assert() by default. -// #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString) - -// Define for C++11 variadic templates which make the code shorter & more -// general. If you don't define this, C++11 support is autodetected below. -// #define TINYFORMAT_USE_VARIADIC_TEMPLATES - - -//------------------------------------------------------------------------------ -// Implementation details. -#include -#include -#include -#include - -#ifndef TINYFORMAT_ERROR -# define TINYFORMAT_ERROR(reason) assert(0 && reason) -#endif - -#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) -# ifdef __GXX_EXPERIMENTAL_CXX0X__ -# define TINYFORMAT_USE_VARIADIC_TEMPLATES -# endif -#endif - -#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 -// std::showpos is broken on old libstdc++ as provided with OSX. See -// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html -# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND -#endif - -#ifdef __APPLE__ -// Workaround OSX linker warning: xcode uses different default symbol -// visibilities for static libs vs executables (see issue #25) -# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) -#else -# define TINYFORMAT_HIDDEN -#endif - -namespace tinyformat { - -//------------------------------------------------------------------------------ -namespace detail { - -// Test whether type T1 is convertible to type T2 -template -struct is_convertible -{ - private: - // two types of different size - struct fail { char dummy[2]; }; - struct succeed { char dummy; }; - // Try to convert a T1 to a T2 by plugging into tryConvert - static fail tryConvert(...); - static succeed tryConvert(const T2&); - static const T1& makeT1(); - public: -# ifdef _MSC_VER - // Disable spurious loss of precision warnings in tryConvert(makeT1()) -# pragma warning(push) -# pragma warning(disable:4244) -# pragma warning(disable:4267) -# endif - // Standard trick: the (...) version of tryConvert will be chosen from - // the overload set only if the version taking a T2 doesn't match. - // Then we compare the sizes of the return types to check which - // function matched. Very neat, in a disgusting kind of way :) - static const bool value = - sizeof(tryConvert(makeT1())) == sizeof(succeed); -# ifdef _MSC_VER -# pragma warning(pop) -# endif -}; - - -// Detect when a type is not a wchar_t string -template struct is_wchar { typedef int tinyformat_wchar_is_not_supported; }; -template<> struct is_wchar {}; -template<> struct is_wchar {}; -template struct is_wchar {}; -template struct is_wchar {}; - - -// Format the value by casting to type fmtT. This default implementation -// should never be called. -template::value> -struct formatValueAsType -{ - static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } -}; -// Specialized version for types that can actually be converted to fmtT, as -// indicated by the "convertible" template parameter. -template -struct formatValueAsType -{ - static void invoke(std::ostream& out, const T& value) - { out << static_cast(value); } -}; - -#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND -template::value> -struct formatZeroIntegerWorkaround -{ - static bool invoke(std::ostream& /**/, const T& /**/) { return false; } -}; -template -struct formatZeroIntegerWorkaround -{ - static bool invoke(std::ostream& out, const T& value) - { - if (static_cast(value) == 0 && out.flags() & std::ios::showpos) - { - out << "+0"; - return true; - } - return false; - } -}; -#endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - -// Convert an arbitrary type to integer. The version with convertible=false -// throws an error. -template::value> -struct convertToInt -{ - static int invoke(const T& /*value*/) - { - TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to " - "integer for use as variable width or precision"); - return 0; - } -}; -// Specialization for convertToInt when conversion is possible -template -struct convertToInt -{ - static int invoke(const T& value) { return static_cast(value); } -}; - -// Format at most ntrunc characters to the given stream. -template -inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) -{ - std::ostringstream tmp; - tmp << value; - std::string result = tmp.str(); - out.write(result.c_str(), (std::min)(ntrunc, static_cast(result.size()))); -} -#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \ -inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ -{ \ - std::streamsize len = 0; \ - while(len < ntrunc && value[len] != 0) \ - ++len; \ - out.write(value, len); \ -} -// Overload for const char* and char*. Could overload for signed & unsigned -// char too, but these are technically unneeded for printf compatibility. -TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char) -TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char) -#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR - -} // namespace detail - - -//------------------------------------------------------------------------------ -// Variable formatting functions. May be overridden for user-defined types if -// desired. - - -/// Format a value into a stream, delegating to operator<< by default. -/// -/// Users may override this for their own types. When this function is called, -/// the stream flags will have been modified according to the format string. -/// The format specification is provided in the range [fmtBegin, fmtEnd). For -/// truncating conversions, ntrunc is set to the desired maximum number of -/// characters, for example "%.7s" calls formatValue with ntrunc = 7. -/// -/// By default, formatValue() uses the usual stream insertion operator -/// operator<< to format the type T, with special cases for the %c and %p -/// conversions. - -template -inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, - const char* fmtEnd, int ntrunc, const T& value) -{ -#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS - // Since we don't support printing of wchar_t using "%ls", make it fail at - // compile time in preference to printing as a void* at runtime. - typedef typename detail::is_wchar::tinyformat_wchar_is_not_supported DummyType; - (void) DummyType(); // avoid unused type warning with gcc-4.8 -#endif - // The mess here is to support the %c and %p conversions: if these - // conversions are active we try to convert the type to a char or const - // void* respectively and format that instead of the value itself. For the - // %p conversion it's important to avoid dereferencing the pointer, which - // could otherwise lead to a crash when printing a dangling (const char*). - const bool canConvertToChar = detail::is_convertible::value; - const bool canConvertToVoidPtr = detail::is_convertible::value; - if(canConvertToChar && *(fmtEnd-1) == 'c') - detail::formatValueAsType::invoke(out, value); - else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') - detail::formatValueAsType::invoke(out, value); -#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; -#endif - else if(ntrunc >= 0) - { - // Take care not to overread C strings in truncating conversions like - // "%.4s" where at most 4 characters may be read. - detail::formatTruncated(out, value, ntrunc); - } - else - out << value; -} - - -// Overloaded version for char types to support printing as an integer -#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ -inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ - const char* fmtEnd, int /**/, charType value) \ -{ \ - switch(*(fmtEnd-1)) \ - { \ - case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ - out << static_cast(value); break; \ - default: \ - out << value; break; \ - } \ -} -// per 3.9.1: char, signed char and unsigned char are all distinct types -TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) -TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) -TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) -#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR - - -//------------------------------------------------------------------------------ -// Tools for emulating variadic templates in C++98. The basic idea here is -// stolen from the boost preprocessor metaprogramming library and cut down to -// be just general enough for what we need. - -#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n -#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n -#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n -#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n - -// To keep it as transparent as possible, the macros below have been generated -// using python via the excellent cog.py code generation script. This avoids -// the need for a bunch of complex (but more general) preprocessor tricks as -// used in boost.preprocessor. -// -// To rerun the code generation in place, use `cog.py -r tinyformat.h` -// (see http://nedbatchelder.com/code/cog). Alternatively you can just create -// extra versions by hand. - -/*[[[cog -maxParams = 16 - -def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): - for j in range(startInd,maxParams+1): - list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) - cog.outl(lineTemplate % {'j':j, 'list':list}) - -makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', - 'class T%(i)d') - -cog.outl() -makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', - 'const T%(i)d& v%(i)d') - -cog.outl() -makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') - -cog.outl() -cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') -makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', - 'v%(i)d', startInd = 2) - -cog.outl() -cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + - ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) -]]]*/ -#define TINYFORMAT_ARGTYPES_1 class T1 -#define TINYFORMAT_ARGTYPES_2 class T1, class T2 -#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 -#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 -#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 -#define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6 -#define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7 -#define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 -#define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 -#define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10 -#define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11 -#define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12 -#define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13 -#define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14 -#define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 -#define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16 - -#define TINYFORMAT_VARARGS_1 const T1& v1 -#define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2 -#define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3 -#define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4 -#define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5 -#define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6 -#define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7 -#define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8 -#define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9 -#define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10 -#define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11 -#define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12 -#define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13 -#define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14 -#define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15 -#define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16 - -#define TINYFORMAT_PASSARGS_1 v1 -#define TINYFORMAT_PASSARGS_2 v1, v2 -#define TINYFORMAT_PASSARGS_3 v1, v2, v3 -#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 -#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 -#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 -#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 -#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 -#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 -#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 -#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 -#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 -#define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 -#define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 -#define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 -#define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 - -#define TINYFORMAT_PASSARGS_TAIL_1 -#define TINYFORMAT_PASSARGS_TAIL_2 , v2 -#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 -#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 -#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 -#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 -#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 -#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 -#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 -#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 -#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 -#define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 -#define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 -#define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 -#define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 -#define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 - -#define TINYFORMAT_FOREACH_ARGNUM(m) \ - m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16) -//[[[end]]] - - - -namespace detail { - -// Type-opaque holder for an argument to format(), with associated actions on -// the type held as explicit function pointers. This allows FormatArg's for -// each argument to be allocated as a homogenous array inside FormatList -// whereas a naive implementation based on inheritance does not. -class FormatArg -{ - public: - FormatArg() {} - - template - FormatArg(const T& value) - : m_value(static_cast(&value)), - m_formatImpl(&formatImpl), - m_toIntImpl(&toIntImpl) - { } - - void format(std::ostream& out, const char* fmtBegin, - const char* fmtEnd, int ntrunc) const - { - m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); - } - - int toInt() const - { - return m_toIntImpl(m_value); - } - - private: - template - TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin, - const char* fmtEnd, int ntrunc, const void* value) - { - formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast(value)); - } - - template - TINYFORMAT_HIDDEN static int toIntImpl(const void* value) - { - return convertToInt::invoke(*static_cast(value)); - } - - const void* m_value; - void (*m_formatImpl)(std::ostream& out, const char* fmtBegin, - const char* fmtEnd, int ntrunc, const void* value); - int (*m_toIntImpl)(const void* value); -}; - - -// Parse and return an integer from the string c, as atoi() -// On return, c is set to one past the end of the integer. -inline int parseIntAndAdvance(const char*& c) -{ - int i = 0; - for(;*c >= '0' && *c <= '9'; ++c) - i = 10*i + (*c - '0'); - return i; -} - -// Print literal part of format string and return next format spec -// position. -// -// Skips over any occurrences of '%%', printing a literal '%' to the -// output. The position of the first % character of the next -// nontrivial format spec is returned, or the end of string. -inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) -{ - const char* c = fmt; - for(;; ++c) - { - switch(*c) - { - case '\0': - out.write(fmt, c - fmt); - return c; - case '%': - out.write(fmt, c - fmt); - if(*(c+1) != '%') - return c; - // for "%%", tack trailing % onto next literal section. - fmt = ++c; - break; - default: - break; - } - } -} - - -// Parse a format string and set the stream state accordingly. -// -// The format mini-language recognized here is meant to be the one from C99, -// with the form "%[flags][width][.precision][length]type". -// -// Formatting options which can't be natively represented using the ostream -// state are returned in spacePadPositive (for space padded positive numbers) -// and ntrunc (for truncating conversions). argIndex is incremented if -// necessary to pull out variable width and precision . The function returns a -// pointer to the character after the end of the current format spec. -inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, - int& ntrunc, const char* fmtStart, - const detail::FormatArg* formatters, - int& argIndex, int numFormatters) -{ - if(*fmtStart != '%') - { - TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); - return fmtStart; - } - // Reset stream state to defaults. - out.width(0); - out.precision(6); - out.fill(' '); - // Reset most flags; ignore irrelevant unitbuf & skipws. - out.unsetf(std::ios::adjustfield | std::ios::basefield | - std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | - std::ios::showpoint | std::ios::showpos | std::ios::uppercase); - bool precisionSet = false; - bool widthSet = false; - int widthExtra = 0; - const char* c = fmtStart + 1; - // 1) Parse flags - for(;; ++c) - { - switch(*c) - { - case '#': - out.setf(std::ios::showpoint | std::ios::showbase); - continue; - case '0': - // overridden by left alignment ('-' flag) - if(!(out.flags() & std::ios::left)) - { - // Use internal padding so that numeric values are - // formatted correctly, eg -00010 rather than 000-10 - out.fill('0'); - out.setf(std::ios::internal, std::ios::adjustfield); - } - continue; - case '-': - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - continue; - case ' ': - // overridden by show positive sign, '+' flag. - if(!(out.flags() & std::ios::showpos)) - spacePadPositive = true; - continue; - case '+': - out.setf(std::ios::showpos); - spacePadPositive = false; - widthExtra = 1; - continue; - default: - break; - } - break; - } - // 2) Parse width - if(*c >= '0' && *c <= '9') - { - widthSet = true; - out.width(parseIntAndAdvance(c)); - } - if(*c == '*') - { - widthSet = true; - int width = 0; - if(argIndex < numFormatters) - width = formatters[argIndex++].toInt(); - else - TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); - if(width < 0) - { - // negative widths correspond to '-' flag set - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - width = -width; - } - out.width(width); - ++c; - } - // 3) Parse precision - if(*c == '.') - { - ++c; - int precision = 0; - if(*c == '*') - { - ++c; - if(argIndex < numFormatters) - precision = formatters[argIndex++].toInt(); - else - TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision"); - } - else - { - if(*c >= '0' && *c <= '9') - precision = parseIntAndAdvance(c); - else if(*c == '-') // negative precisions ignored, treated as zero. - parseIntAndAdvance(++c); - } - out.precision(precision); - precisionSet = true; - } - // 4) Ignore any C99 length modifier - while(*c == 'l' || *c == 'h' || *c == 'L' || - *c == 'j' || *c == 'z' || *c == 't') - ++c; - // 5) We're up to the conversion specifier character. - // Set stream flags based on conversion specifier (thanks to the - // boost::format class for forging the way here). - bool intConversion = false; - switch(*c) - { - case 'u': case 'd': case 'i': - out.setf(std::ios::dec, std::ios::basefield); - intConversion = true; - break; - case 'o': - out.setf(std::ios::oct, std::ios::basefield); - intConversion = true; - break; - case 'X': - out.setf(std::ios::uppercase); - // Falls through - case 'x': case 'p': - out.setf(std::ios::hex, std::ios::basefield); - intConversion = true; - break; - case 'E': - out.setf(std::ios::uppercase); - // Falls through - case 'e': - out.setf(std::ios::scientific, std::ios::floatfield); - out.setf(std::ios::dec, std::ios::basefield); - break; - case 'F': - out.setf(std::ios::uppercase); - // Falls through - case 'f': - out.setf(std::ios::fixed, std::ios::floatfield); - break; - case 'G': - out.setf(std::ios::uppercase); - // Falls through - case 'g': - out.setf(std::ios::dec, std::ios::basefield); - // As in boost::format, let stream decide float format. - out.flags(out.flags() & ~std::ios::floatfield); - break; - case 'a': case 'A': - TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " - "are not supported"); - break; - case 'c': - // Handled as special case inside formatValue() - break; - case 's': - if(precisionSet) - ntrunc = static_cast(out.precision()); - // Make %s print booleans as "true" and "false" - out.setf(std::ios::boolalpha); - break; - case 'n': - // Not supported - will cause problems! - TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); - break; - case '\0': - TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " - "terminated by end of string"); - return c; - default: - break; - } - if(intConversion && precisionSet && !widthSet) - { - // "precision" for integers gives the minimum number of digits (to be - // padded with zeros on the left). This isn't really supported by the - // iostreams, but we can approximately simulate it with the width if - // the width isn't otherwise used. - out.width(out.precision() + widthExtra); - out.setf(std::ios::internal, std::ios::adjustfield); - out.fill('0'); - } - return c+1; -} - - -//------------------------------------------------------------------------------ -inline void formatImpl(std::ostream& out, const char* fmt, - const detail::FormatArg* formatters, - int numFormatters) -{ - // Saved stream state - std::streamsize origWidth = out.width(); - std::streamsize origPrecision = out.precision(); - std::ios::fmtflags origFlags = out.flags(); - char origFill = out.fill(); - - for (int argIndex = 0; argIndex < numFormatters; ++argIndex) - { - // Parse the format string - fmt = printFormatStringLiteral(out, fmt); - bool spacePadPositive = false; - int ntrunc = -1; - const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, - formatters, argIndex, numFormatters); - if (argIndex >= numFormatters) - { - // Check args remain after reading any variable width/precision - TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); - return; - } - const FormatArg& arg = formatters[argIndex]; - // Format the arg into the stream. - if(!spacePadPositive) - arg.format(out, fmt, fmtEnd, ntrunc); - else - { - // The following is a special case with no direct correspondence - // between stream formatting and the printf() behaviour. Simulate - // it crudely by formatting into a temporary string stream and - // munging the resulting string. - std::ostringstream tmpStream; - tmpStream.copyfmt(out); - tmpStream.setf(std::ios::showpos); - arg.format(tmpStream, fmt, fmtEnd, ntrunc); - std::string result = tmpStream.str(); // allocates... yuck. - for(size_t i = 0, iend = result.size(); i < iend; ++i) - if(result[i] == '+') result[i] = ' '; - out << result; - } - fmt = fmtEnd; - } - - // Print remaining part of format string. - fmt = printFormatStringLiteral(out, fmt); - if(*fmt != '\0') - { - TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); - } - - // Restore stream state - out.width(origWidth); - out.precision(origPrecision); - out.flags(origFlags); - out.fill(origFill); -} - -} // namespace detail - - -/// List of template arguments format(), held in a type-opaque way. -/// -/// A const reference to FormatList (typedef'd as FormatListRef) may be -/// conveniently used to pass arguments to non-template functions: All type -/// information has been stripped from the arguments, leaving just enough of a -/// common interface to perform formatting as required. -class FormatList -{ - public: - FormatList(detail::FormatArg* formatters, int N) - : m_formatters(formatters), m_N(N) { } - - friend void vformat(std::ostream& out, const char* fmt, - const FormatList& list); - - private: - const detail::FormatArg* m_formatters; - int m_N; -}; - -/// Reference to type-opaque format list for passing to vformat() -typedef const FormatList& FormatListRef; - - -namespace detail { - -// Format list subclass with fixed storage to avoid dynamic allocation -template -class FormatListN : public FormatList -{ - public: -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - template - FormatListN(const Args&... args) - : FormatList(&m_formatterStore[0], N), - m_formatterStore { FormatArg(args)... } - { static_assert(sizeof...(args) == N, "Number of args must be N"); } -#else // C++98 version - void init(int) {} -# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ - \ - template \ - FormatListN(TINYFORMAT_VARARGS(n)) \ - : FormatList(&m_formatterStore[0], n) \ - { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ - \ - template \ - void init(int i, TINYFORMAT_VARARGS(n)) \ - { \ - m_formatterStore[i] = FormatArg(v1); \ - init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ - } - - TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) -# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR -#endif - - private: - FormatArg m_formatterStore[N]; -}; - -// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard -template<> class FormatListN<0> : public FormatList -{ - public: FormatListN() : FormatList(0, 0) {} -}; - -} // namespace detail - - -//------------------------------------------------------------------------------ -// Primary API functions - -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - -/// Make type-agnostic format list from list of template arguments. -/// -/// The exact return type of this function is an implementation detail and -/// shouldn't be relied upon. Instead it should be stored as a FormatListRef: -/// -/// FormatListRef formatList = makeFormatList( /*...*/ ); -template -detail::FormatListN makeFormatList(const Args&... args) -{ - return detail::FormatListN(args...); -} - -#else // C++98 version - -inline detail::FormatListN<0> makeFormatList() -{ - return detail::FormatListN<0>(); -} -#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \ -template \ -detail::FormatListN makeFormatList(TINYFORMAT_VARARGS(n)) \ -{ \ - return detail::FormatListN(TINYFORMAT_PASSARGS(n)); \ -} -TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) -#undef TINYFORMAT_MAKE_MAKEFORMATLIST - -#endif - -/// Format list of arguments to the stream according to the given format string. -/// -/// The name vformat() is chosen for the semantic similarity to vprintf(): the -/// list of format arguments is held in a single function argument. -inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) -{ - detail::formatImpl(out, fmt, list.m_formatters, list.m_N); -} - - -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - -/// Format list of arguments to the stream according to given format string. -template -void format(std::ostream& out, const char* fmt, const Args&... args) -{ - vformat(out, fmt, makeFormatList(args...)); -} - -/// Format list of arguments according to the given format string and return -/// the result as a string. -template -std::string format(const char* fmt, const Args&... args) -{ - std::ostringstream oss; - format(oss, fmt, args...); - return oss.str(); -} - -/// Format list of arguments to std::cout, according to the given format string -template -void printf(const char* fmt, const Args&... args) -{ - format(std::cout, fmt, args...); -} - -template -void printfln(const char* fmt, const Args&... args) -{ - format(std::cout, fmt, args...); - std::cout << '\n'; -} - - -#else // C++98 version - -inline void format(std::ostream& out, const char* fmt) -{ - vformat(out, fmt, makeFormatList()); -} - -inline std::string format(const char* fmt) -{ - std::ostringstream oss; - format(oss, fmt); - return oss.str(); -} - -inline void printf(const char* fmt) -{ - format(std::cout, fmt); -} - -inline void printfln(const char* fmt) -{ - format(std::cout, fmt); - std::cout << '\n'; -} - -#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ - \ -template \ -void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \ -} \ - \ -template \ -std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - std::ostringstream oss; \ - format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ - return oss.str(); \ -} \ - \ -template \ -void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ -} \ - \ -template \ -void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ - std::cout << '\n'; \ -} - -TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) -#undef TINYFORMAT_MAKE_FORMAT_FUNCS - -#endif - - -} // namespace tinyformat - -#endif // TINYFORMAT_H_INCLUDED diff --git a/makefile b/makefile index fcf2d8a1..9220ead3 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,9 @@ -WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wno-deprecated-declarations -Wno-init-list-lifetime +WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wno-deprecated-declarations + +GCCWARNINGS := -Wno-init-list-lifetime CLANGWARNINGS := -Wno-undefined-func-template -Wno-comma -Wno-nullability-completeness -Wno-redundant-move -Wno-nested-anon-types -Wno-gnu-anonymous-struct -Wno-reserved-id-macro -Wno-extra-semi -Wno-gnu-zero-variadic-macro-arguments -Wno-shift-sign-overflow -Wno-exit-time-destructors -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-documentation-unknown-command -Wno-weak-vtables -Wno-c++98-compat -Wold-style-cast @@ -23,6 +25,7 @@ LLVM_CONFIG ?= "llvm-config" CXXSRC := $(shell find source external -iname "*.cpp") CXXOBJ := $(CXXSRC:.cpp=.cpp.o) +CXXDEPS := $(CXXSRC:.cpp=.cpp.d) PRECOMP_HDRS := source/include/precompile.h PRECOMP_GCH := $(PRECOMP_HDRS:.h=.h.gch) @@ -30,7 +33,6 @@ PRECOMP_GCH := $(PRECOMP_HDRS:.h=.h.gch) FLXLIBLOCATION := $(SYSROOT)/$(PREFIX)/lib FLXSRC := $(shell find libs -iname "*.flx") -CXXDEPS := $(CXXSRC:.cpp=.cpp.d) NUMFILES := $$(($(words $(CXXSRC)))) @@ -68,8 +70,10 @@ CXXFLAGS += $(MPFR_CFLAGS) $(LIBFFI_CFLAGS) LDFLAGS += $(MPFR_LDFLAGS) $(LIBFFI_LDFLAGS) ifneq (,$(findstring clang,$(COMPILER_IDENT))) - CXXFLAGS += -Wall -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) + CXXFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) CFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) +else + CXXFLAGS += (GCCWARNINGS) endif @@ -141,21 +145,15 @@ $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) @printf "# compiling [$(words $(DONEFILES))/$(NUMFILES)] $<\n" @$(CXX) $(CXXFLAGS) $(WARNINGS) -include source/include/precompile.h -Isource/include -Iexternal -I$(shell $(LLVM_CONFIG) --includedir) -MMD -MP -o $@ $< -$(UTF8REWIND_AR): - @make -C external/utf8rewind all - - -%.c.o: %.c - @$(eval DONEFILES += "C") - @printf "# compiling [$(words $(DONEFILES))/$(NUMFILES)] $<\n" - @$(CC) $(CFLAGS) $(WARNINGS) -Iexternal/utf8rewind/include/utf8rewind -MMD -MP -o $@ $< - - %.h.gch: %.h @printf "# precompiling header $<\n" @$(CXX) $(CXXFLAGS) $(WARNINGS) -o $@ $< +$(UTF8REWIND_AR): + @make -C external/utf8rewind all + + # haha clena: clean clean: diff --git a/source/codegen/arithmetic.cpp b/source/codegen/arithmetic.cpp index 1b1d4fde..c0d9e9b4 100644 --- a/source/codegen/arithmetic.cpp +++ b/source/codegen/arithmetic.cpp @@ -49,7 +49,7 @@ namespace sst if(auto parent = target->toUnionVariantType()->getParentUnion(); parent != vt) { error(this, "unwrapping union of type '%s' to variant ('%s') of unrelated union '%s'", - vt, target->toUnionVariantType()->getName(), dcast(fir::Type, parent)); + vt, target->toUnionVariantType()->getName(), parent); } else { diff --git a/source/codegen/constructor.cpp b/source/codegen/constructor.cpp index 55f74232..f22650e4 100644 --- a/source/codegen/constructor.cpp +++ b/source/codegen/constructor.cpp @@ -118,7 +118,7 @@ fir::Value* cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, if(vargs.size() != wrapper_func->getArgumentCount()) { SimpleError::make(this->loc(), "mismatched number of arguments in constructor call to class '%s'; expected %d, found %d instead", - dcast(fir::Type, cls), constrfn->getArgumentCount(), vargs.size()) + cls, constrfn->getArgumentCount(), vargs.size()) ->append(SimpleError::make(MsgType::Note, constr->loc, "constructor was defined here:")) ->postAndQuit(); } diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 0f343d67..17fb7c51 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -410,7 +410,7 @@ namespace fir else { error("no method named '%s' matching signature '%s' in virtual method table of class '%s'", - name, dcast(Type, ft), this->getTypeName().name); + name, ft, this->getTypeName().name); } } diff --git a/source/fir/Types/SingleTypes.cpp b/source/fir/Types/SingleTypes.cpp index 1944100d..4c3cd592 100644 --- a/source/fir/Types/SingleTypes.cpp +++ b/source/fir/Types/SingleTypes.cpp @@ -113,7 +113,7 @@ namespace fir if(cnt->isFloating()) { if(cnt->getMinBits() > 64) - error("constant number type '%s' requires too many bits", dcast(Type, cnt)); + error("constant number type '%s' requires too many bits", cnt); return fir::Type::getFloat64(); } @@ -125,12 +125,12 @@ namespace fir } else if(cnt->isSigned()) { - error("constant number type '%s' requires too many bits", dcast(Type, cnt)); + error("constant number type '%s' requires too many bits", cnt); } else { if(cnt->getMinBits() > fir::Type::getNativeUWord()->getBitWidth()) - error("constant number type '%s' requires too many bits", dcast(Type, cnt)); + error("constant number type '%s' requires too many bits", cnt); return fir::Type::getNativeUWord(); } diff --git a/source/include/defs.h b/source/include/defs.h index 6dbf6d0f..3a91ded4 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -80,29 +80,12 @@ std::string strprintf(const char* fmt, Ts&&... ts) #define TAB_WIDTH 4 - #define dcast(t, v) dynamic_cast(v) -#define USE_SKA_HASHMAP false -#if USE_SKA_HASHMAP - #include "ska/flat_hash_map.hpp" -#endif - namespace util { - #if USE_SKA_HASHMAP - using hash_map = ska::flat_hash_map; - - template - std::vector> pairs(const util::hash_map& map) - { - auto ret = std::vector>(map.begin(), map.end()); - return ret; - } - #else - template - using hash_map = std::unordered_map; - #endif + template + using hash_map = std::unordered_map; } namespace fir diff --git a/source/platform/backtrace.cpp b/source/platform/backtrace.cpp index 1e28b99a..c10070a5 100644 --- a/source/platform/backtrace.cpp +++ b/source/platform/backtrace.cpp @@ -106,6 +106,7 @@ namespace platform } else { + // TODO. } } #endif diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 74cb8b6e..0d499f24 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -288,8 +288,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(!args[0]->type->isCharType() && !args[0]->type->isStringType() && !args[0]->type->isCharSliceType()) { error(fc, "invalid argument type '%s' to builtin string method 'append'; expected one of '%s', '%s', or '%s'", - args[0]->type, fir::Type::getInt8(), dcast(fir::Type, fir::Type::getCharSlice(false)), - dcast(fir::Type, fir::Type::getString())); + args[0]->type, fir::Type::getInt8(), fir::Type::getCharSlice(false), fir::Type::getString()); } } @@ -370,7 +369,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { error(fc, "invalid argument type '%s' to builtin array method 'append'; expected one of '%s', '%s', or '%s'", args[0]->type, type, type->getArrayElementType(), - dcast(fir::Type, fir::ArraySliceType::get(type->getArrayElementType(), false))); + fir::ArraySliceType::get(type->getArrayElementType(), false)); } } diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 646fe97e..8b22696c 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -320,7 +320,7 @@ namespace resolver else if(auto t2 = arguments[1].value->type; fir::getCastDistance(t2, fir::Type::getNativeWord()) < 0) { error(arguments[0].loc, "second argument to two-arg string initialiser (length) must be '%s', found '%s' instead", - dcast(fir::Type, fir::Type::getNativeWord()), t2); + fir::Type::getNativeWord(), t2); } else { diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index 865ff48d..ebca07ab 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -204,7 +204,7 @@ void checkTraitConformity(sst::TypecheckState* fs, sst::TypeDefn* defn) for(const auto& m : missings) { err->append(SimpleError::make(MsgType::Note, std::get<0>(m), "missing implementation for method '%s': %s:", - std::get<1>(m), dcast(fir::Type, std::get<2>(m)))); + std::get<1>(m), std::get<2>(m))); } err->append( From 14632f3b7bfafaff06d7f646b48c74c6d837d906 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 13:45:10 +0800 Subject: [PATCH 020/129] oh no (9) --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 9220ead3..e8be7448 100644 --- a/makefile +++ b/makefile @@ -73,7 +73,7 @@ ifneq (,$(findstring clang,$(COMPILER_IDENT))) CXXFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) CFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) else - CXXFLAGS += (GCCWARNINGS) + CXXFLAGS += $(GCCWARNINGS) endif From 581ad07e7831afd8706ca72496286cf4a8cd3ea1 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 14:14:03 +0800 Subject: [PATCH 021/129] oh no (10) --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a887a888..d7f0fd3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ matrix: dist: xenial sudo: false - os: osx - osx_image: xcode11.1 + osx_image: xcode11.2 cache: directories: @@ -34,6 +34,8 @@ script: curl 'https://homebrew.bintray.com/bottles/llvm@7-7.1.0.high_sierra.bottle.tar.gz' -L -o llvm-7.1.0.tar.gz; tar -xf llvm-7.1.0.tar.gz; fi; + echo "clang version:" $($CC --version); + echo "clang path:" $(which $CC); PATH="$PATH:$(pwd)/llvm@7/7.1.0/bin" LLVM_CONFIG=llvm-config make -j2 build; else CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-7 make -j2 build; From efb3fe6c1545a017f31255fac7229510d1d5bc9b Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 17 Oct 2019 14:57:06 +0800 Subject: [PATCH 022/129] oh no (11) --- makefile | 2 +- source/include/zpr.h | 50 ++++++++++++++++++++++++++++++++++---------- source/main.cpp | 1 + 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/makefile b/makefile index e8be7448..7d52cabc 100644 --- a/makefile +++ b/makefile @@ -137,7 +137,7 @@ copylibs: $(FLXSRC) $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal $(shell $(LLVM_CONFIG) --cxxflags --ldflags --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind %.cpp.o: %.cpp diff --git a/source/include/zpr.h b/source/include/zpr.h index a6e01154..4dae857e 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -309,8 +308,8 @@ namespace zpr { int base = 10; if(args.specifier == 'x' || args.specifier == 'X') base = 16; - else if(args.specifier == 'o') base = 8; - else if(args.specifier == 'b') base = 2; + // else if(args.specifier == 'o') base = 8; + // else if(args.specifier == 'b') base = 2; // handle negative values ourselves btw, due to padding bool is_neg = false; @@ -327,17 +326,46 @@ namespace zpr char buf[65] = {0}; size_t digits_len = 0; + const char* fmt_str = 0; + auto spec = args.specifier; - std::to_chars_result ret; - if constexpr (std::is_enum_v) - ret = std::to_chars(&buf[0], &buf[65], static_cast>(x), /* base: */ base); + switch(args.length) + { + case format_args::LENGTH_SHORT_SHORT: fmt_str = (spec == 'u' + ? "%hhu" : (spec == 'x' ? "%hhx" : (spec == 'X' ? "%hhX" : "%hhd"))); break; + case format_args::LENGTH_SHORT: fmt_str = (spec == 'u' + ? "%hu" : (spec == 'x' ? "%hx" : (spec == 'X' ? "%hX" : "%hd"))); break; + case format_args::LENGTH_LONG: fmt_str = (spec == 'u' + ? "%lu" : (spec == 'x' ? "%lx" : (spec == 'X' ? "%lX" : "%ld"))); break; + case format_args::LENGTH_LONG_LONG: fmt_str = (spec == 'u' + ? "%llu" : (spec == 'x' ? "%llx" : (spec == 'X' ? "%llX" : "%lld"))); break; + case format_args::LENGTH_INTMAX_T: fmt_str = (spec == 'u' + ? "%ju" : (spec == 'x' ? "%jx" : (spec == 'X' ? "%jX" : "%jd"))); break; + case format_args::LENGTH_SIZE_T: fmt_str = (spec == 'u' + ? "%zu" : (spec == 'x' ? "%zx" : (spec == 'X' ? "%zX" : "%zd"))); break; + case format_args::LENGTH_PTRDIFF_T: fmt_str = (spec == 'u' + ? "%tu" : (spec == 'x' ? "%tx" : (spec == 'X' ? "%tX" : "%td"))); break; + + case format_args::LENGTH_DEFAULT: [[fallthrough]]; + default: + fmt_str = (spec == 'u' ? "%u" : (spec == 'x' ? "%x" : (spec == 'X' ? "%X" : "%d"))); + break; + } - else - ret = std::to_chars(&buf[0], &buf[65], x, /* base: */ base); + digits_len = snprintf(&buf[0], 64, fmt_str, x); + + // sadly, we must cheat here as well, because osx doesn't bloody have charconv (STILL)? + + // std::to_chars_result ret; + // if constexpr (std::is_enum_v) + // ret = std::to_chars(&buf[0], &buf[65], static_cast>(x), /* base: */ base); + + // else + // ret = std::to_chars(&buf[0], &buf[65], x, /* base: */ base); - if(ret.ec == std::errc()) digits_len = (ret.ptr - &buf[0]), *ret.ptr = 0; - else return ""; + // if(ret.ec == std::errc()) digits_len = (ret.ptr - &buf[0]), *ret.ptr = 0; + // else return ""; if(isupper(args.specifier)) for(size_t i = 0; i < digits_len; i++) @@ -411,7 +439,7 @@ namespace zpr { constexpr int default_prec = 6; - char buf[80] = { 0 }; + char buf[81] = { 0 }; int64_t num_length = 0; // lmao. nobody except msvc stl (and only the most recent version) implements std::to_chars diff --git a/source/main.cpp b/source/main.cpp index 43fde325..21e897ce 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -29,6 +29,7 @@ struct timer std::chrono::time_point start; }; + static void compile(std::string in, std::string out) { auto start_time = std::chrono::high_resolution_clock::now(); From 2ad7de74bf83cec22a7d282045ee54c1fde53d94 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 18 Oct 2019 15:03:34 +0800 Subject: [PATCH 023/129] add codecov.io coverage. --- .gitignore | 6 +++--- .travis.yml | 20 +++++++++++++++----- README.md | 9 +++------ build/ultratiny.flx | 6 +++--- libs/std/set.flx | 2 +- makefile | 13 ++++++++++--- source/frontend/parser/function.cpp | 1 + 7 files changed, 36 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index b662bdfb..7ccf0f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ *.app *.pdb +# profile data +*.profdata +*.profraw # overrides: @@ -42,9 +45,6 @@ build/sysroot build/meson-* .modifydates -# this is a generated file, so we ignore it. -source/unity.cpp - build/test build/gltest build/standalone diff --git a/.travis.yml b/.travis.yml index d7f0fd3b..ce4d3d54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,19 +36,27 @@ script: fi; echo "clang version:" $($CC --version); echo "clang path:" $(which $CC); - PATH="$PATH:$(pwd)/llvm@7/7.1.0/bin" LLVM_CONFIG=llvm-config make -j2 build; + PATH="$PATH:$(pwd)/llvm@7/7.1.0/bin" LLVM_CONFIG=llvm-config ENABLE_CODE_COVERAGE=yes make -j2 build; else - CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-7 make -j2 build; + CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-7 ENABLE_CODE_COVERAGE=yes make -j2 build; + fi + - LLVM_PROFILE_FILE="tester_llvm.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx + - LLVM_PROFILE_FILE="tester_interp.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx + - LLVM_PROFILE_FILE="tester_compile.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile build/tester.flx && ./tester + - | + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + llvm@7/7.1.0/bin/llvm-profdata merge -sparse tester_llvm.profraw tester_interp.profraw tester_compile.profraw -o tester_all.profdata; + llvm@7/7.1.0/bin/llvm-cov show ./build/sysroot/usr/local/bin/flaxc -instr-profile=tester_all.profdata > coverage.txt; + else + for filename in `find . -name '*.cpp'`; do gcov-9 -o "$filename".gcno $filename > /dev/null; done fi - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile build/tester.flx && ./tester notifications: email: false after_success: - zip -r $TRAVIS_OS_NAME-x64 build/sysroot + - bash <(curl -s https://codecov.io/bash) deploy: provider: releases @@ -58,3 +66,5 @@ deploy: on: all_branches: true tags: true + + diff --git a/README.md b/README.md index 7a184cda..d433d5e4 100644 --- a/README.md +++ b/README.md @@ -176,12 +176,9 @@ The Flax compiler itself (this repository) is licensed under the Apache 2.0 lice are included in the repository itself (under the `external` folder) and compiled together, instead of as a separate library (shared or otherwise). These are: -1. [fmt](https://github.com/fmtlib/fmt), MIT -2. [mpreal](https://bitbucket.org/advanpix/mpreal), GPL -3. [tinyformat](https://github.com/c42f/tinyformat), Boost -4. [utf8rewind](https://bitbucket.org/knight666/utf8rewind), MIT -5. [flat_hash_map](https://github.com/skarupke/flat_hash_map), Boost -6. [tinyprocesslib](https://gitlab.com/eidheim/tiny-process-library), MIT +1. [mpreal](https://bitbucket.org/advanpix/mpreal), GPL +2. [utf8rewind](https://bitbucket.org/knight666/utf8rewind), MIT +3. [tinyprocesslib](https://gitlab.com/eidheim/tiny-process-library), MIT diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 3bd4dcbb..6af29bf9 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -53,7 +53,7 @@ fn stuff2() -> Bar @entry fn main() { let q = stuff() - // let p = stuff2() + let p = stuff2() printf("q = %d\n", q) } @@ -65,9 +65,9 @@ fn stuff2() -> Bar // 1. call the appropriate methods on structs. (kind of: still need Copy and Move) - // 4. implement @compiler_support + // 2. generalise the raii mechanism to by searching for traits instead of hardcoding for classes + (kind of: still need Copy and Move) - 2. generalise the raii mechanism to by searching for traits instead of hardcoding for classes 3. remove the copy/move/destruct stuff from FIR? */ diff --git a/libs/std/set.flx b/libs/std/set.flx index 6dbc2c2f..2440547c 100644 --- a/libs/std/set.flx +++ b/libs/std/set.flx @@ -2,7 +2,7 @@ // Copyright (c) 2017, zhiayang // Licensed under the Apache License Version 2.0. -export std::set +export std public class set { diff --git a/makefile b/makefile index 7d52cabc..5f34204a 100644 --- a/makefile +++ b/makefile @@ -40,7 +40,6 @@ DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fno-exceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) -CFLAGS += -std=c11 -O0 -g -c -Wall -fno-omit-frame-pointer -Wno-overlength-strings $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) @@ -65,15 +64,23 @@ endif MPFR_CFLAGS := $(shell pkg-config --cflags mpfr) MPFR_LDFLAGS := $(shell pkg-config --libs mpfr) -CFLAGS += $(MPFR_CFLAGS) $(LIBFFI_CFLAGS) CXXFLAGS += $(MPFR_CFLAGS) $(LIBFFI_CFLAGS) LDFLAGS += $(MPFR_LDFLAGS) $(LIBFFI_LDFLAGS) +ENABLE_CODE_COVERAGE ?= "yes" + ifneq (,$(findstring clang,$(COMPILER_IDENT))) CXXFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) - CFLAGS += -Xclang -fcolor-diagnostics $(SANITISE) $(CLANGWARNINGS) + ifeq ("$(ENABLE_CODE_COVERAGE)","yes") + CXXFLAGS += -fprofile-instr-generate -fcoverage-mapping + LDFLAGS += -fprofile-instr-generate -fcoverage-mapping + endif else CXXFLAGS += $(GCCWARNINGS) + ifeq ("$(ENABLE_CODE_COVERAGE)","yes") + CXXFLAGS += -fprofile-arcs -ftest-coverage + LDFLAGS += -lgcov + endif endif diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index 090a518d..cb271401 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -166,6 +166,7 @@ namespace parser { iceAssert(st.front() == TT::ForeignFunc); st.pop(); + st.skipWS(); if(st.front() != TT::Func) expectedAfter(st, "'fn'", "'ffi'", st.front().str()); From 8661408346ea0b94bb6bf3796ceb21e74aa524b4 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 18 Oct 2019 15:05:25 +0800 Subject: [PATCH 024/129] add badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d433d5e4..fa8338a7 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ A low level, general-purpose language with high level syntax and expressibility.      [![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax)      +[![codecov](https://codecov.io/gh/flax-lang/flax/branch/develop/graph/badge.svg)](https://codecov.io/gh/flax-lang/flax) +     [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) From c518a4b0afa4191315e55e0eadfcc8efb9420ad1 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 18 Oct 2019 21:15:51 +0800 Subject: [PATCH 025/129] upgrade complete --- makefile | 4 +- source/backend/llvm/jit.cpp | 110 ++++++++++++++++++++++------- source/backend/llvm/linker.cpp | 5 +- source/backend/llvm/translator.cpp | 8 +-- source/include/backends/llvm.h | 36 +++++++--- 5 files changed, 119 insertions(+), 44 deletions(-) diff --git a/makefile b/makefile index 5f34204a..84b861ff 100644 --- a/makefile +++ b/makefile @@ -39,9 +39,9 @@ NUMFILES := $$(($(words $(CXXSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++17 -O0 -g -c -Wall -frtti -fno-exceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -fvisibility=hidden -O0 -g -c -Wall -frtti -fno-exceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) -LDFLAGS += $(SANITISE) +LDFLAGS += $(SANITISE) -fvisibility=hidden diff --git a/source/backend/llvm/jit.cpp b/source/backend/llvm/jit.cpp index 726ce5d1..ce7a0e28 100644 --- a/source/backend/llvm/jit.cpp +++ b/source/backend/llvm/jit.cpp @@ -5,53 +5,111 @@ #include "errors.h" #include "backends/llvm.h" + +#ifdef _MSC_VER + #pragma warning(push, 0) + #pragma warning(disable: 4267) + #pragma warning(disable: 4244) +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +#include "llvm/Analysis/Passes.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Transforms/Scalar/GVN.h" +#include "llvm/Transforms/InstCombine/InstCombine.h" + + +#ifdef _MSC_VER + #pragma warning(pop) +#else + #pragma GCC diagnostic pop +#endif + + + + namespace backend { - LLVMJit::LLVMJit(llvm::TargetMachine* tm) : - targetMachine(tm), - symbolResolver(llvm::orc::createLegacyLookupResolver(this->execSession, [&](const std::string& name) -> llvm::JITSymbol { - if(auto sym = this->compileLayer.findSymbol(name, false)) return sym; - else if(auto err = sym.takeError()) return std::move(err); - - if(auto symaddr = llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name)) - return llvm::JITSymbol(symaddr, llvm::JITSymbolFlags::Exported); - else - return llvm::JITSymbol(nullptr); - }, [](llvm::Error err) { llvm::cantFail(std::move(err), "lookupFlags failed"); })), - dataLayout(this->targetMachine->createDataLayout()), - objectLayer(this->execSession, [this](llvm::orc::VModuleKey) -> auto { - return llvm::orc::RTDyldObjectLinkingLayer::Resources { - std::make_shared(), this->symbolResolver }; }), - compileLayer(this->objectLayer, llvm::orc::SimpleCompiler(*this->targetMachine.get())) + LLVMJit::LLVMJit() : + TM(llvm::EngineBuilder().selectTarget()), DL(TM->createDataLayout()), + ObjectLayer(llvm::AcknowledgeORCv1Deprecation, ES, [this](llvm::orc::VModuleKey K) -> auto { + return llvm::orc::LegacyRTDyldObjectLinkingLayer::Resources { + std::make_shared(), Resolvers[K] + }; + }), + CompileLayer(llvm::AcknowledgeORCv1Deprecation, ObjectLayer, llvm::orc::SimpleCompiler(*TM)), + OptimiseLayer(llvm::AcknowledgeORCv1Deprecation, CompileLayer, [this](std::unique_ptr M) -> auto { + return optimiseModule(std::move(M)); + }), + CompileCallbackManager(cantFail(llvm::orc::createLocalCompileCallbackManager(TM->getTargetTriple(), ES, 0))), + CODLayer(llvm::AcknowledgeORCv1Deprecation, ES, OptimiseLayer, + [&](ModuleHandle_t K) { return Resolvers[K]; }, + [&](ModuleHandle_t K, std::shared_ptr R) { + Resolvers[K] = std::move(R); + }, + [](llvm::Function& F) { return std::set({ &F }); }, + *CompileCallbackManager, llvm::orc::createLocalIndirectStubsManagerBuilder(TM->getTargetTriple())) { llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); } - llvm::TargetMachine* LLVMJit::getTargetMachine() - { - return this->targetMachine.get(); - } - LLVMJit::ModuleHandle_t LLVMJit::addModule(std::unique_ptr mod) { - auto vmod = this->execSession.allocateVModule(); - llvm::cantFail(this->compileLayer.addModule(vmod, std::move(mod))); + auto vmod = this->ES.allocateVModule(); + + this->Resolvers[vmod] = createLegacyLookupResolver(this->ES, [this](const std::string& name) -> llvm::JITSymbol { + if(auto Sym = CompileLayer.findSymbol(name, false)) + return Sym; + + else if(auto Err = Sym.takeError()) + return std::move(Err); + + if(auto SymAddr = llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name)) + return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported); + + return nullptr; + }, [](llvm::Error Err) { llvm::cantFail(std::move(Err), "lookupFlags failed"); }); + + // Add the module to the JIT with the new key. + llvm::cantFail(this->CODLayer.addModule(vmod, std::move(mod))); return vmod; } void LLVMJit::removeModule(LLVMJit::ModuleHandle_t mod) { - llvm::cantFail(this->compileLayer.removeModule(mod)); + llvm::cantFail(this->CODLayer.removeModule(mod)); } llvm::JITSymbol LLVMJit::findSymbol(const std::string& name) { std::string mangledName; llvm::raw_string_ostream out(mangledName); - llvm::Mangler::getNameWithPrefix(out, name, this->dataLayout); + llvm::Mangler::getNameWithPrefix(out, name, this->DL); + + return this->CODLayer.findSymbol(out.str(), false); + } + + std::unique_ptr LLVMJit::optimiseModule(std::unique_ptr mod) + { + // Create a function pass manager. + auto fpm = llvm::make_unique(mod.get()); + + // Add some optimisations. + fpm->add(llvm::createInstructionCombiningPass()); + fpm->add(llvm::createReassociatePass()); + fpm->add(llvm::createGVNPass()); + fpm->add(llvm::createCFGSimplificationPass()); + fpm->doInitialization(); + + // Run the optimizations over all functions in the module being added to the JIT. + for(auto& F : *mod) + fpm->run(F); - return this->compileLayer.findSymbol(out.str(), false); + return mod; } llvm::JITTargetAddress LLVMJit::getSymbolAddress(const std::string& name) diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index 757f3d9d..6c7c629a 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -43,8 +43,9 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/DynamicLibrary.h" -#include "llvm/Transforms/InstCombine/InstCombine.h" +#include "llvm/Transforms/Scalar/Scalarizer.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Transforms/InstCombine/InstCombine.h" #ifdef _MSC_VER #pragma warning(pop) @@ -440,7 +441,7 @@ namespace backend { auto name = this->entryFunction->getName().str(); - this->jitInstance = new LLVMJit(this->targetMachine); + this->jitInstance = new LLVMJit(); this->jitInstance->addModule(std::move(this->linkedModule)); auto entryaddr = this->jitInstance->getSymbolAddress(name); diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 790e5ec1..23fd6751 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -684,7 +684,7 @@ namespace backend for(auto intr : firmod->_getIntrinsicFunctions()) { - llvm::Constant* fn = 0; + llvm::Value* fn = 0; //* in LLVM 7, the intrinsics changed to no longer specify the alignment //* so, the arugments are: [ ptr, ptr, size, is_volatile ] @@ -692,19 +692,19 @@ namespace backend { llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(gc), { llvm::Type::getInt8PtrTy(gc), llvm::Type::getInt8PtrTy(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); - fn = module->getOrInsertFunction(strprintf("llvm.memcpy.p0i8.p0i8.i%d", fir::getNativeWordSizeInBits()), ft); + fn = module->getOrInsertFunction(strprintf("llvm.memcpy.p0i8.p0i8.i%d", fir::getNativeWordSizeInBits()), ft).getCallee(); } else if(intr.first.str() == "memmove") { llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(gc), { llvm::Type::getInt8PtrTy(gc), llvm::Type::getInt8PtrTy(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); - fn = module->getOrInsertFunction(strprintf("llvm.memmove.p0i8.p0i8.i%d", fir::getNativeWordSizeInBits()), ft); + fn = module->getOrInsertFunction(strprintf("llvm.memmove.p0i8.p0i8.i%d", fir::getNativeWordSizeInBits()), ft).getCallee(); } else if(intr.first.str() == "memset") { llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(gc), { llvm::Type::getInt8PtrTy(gc), llvm::Type::getInt8Ty(gc), getNativeWordTy(), llvm::Type::getInt1Ty(gc) }, false); - fn = module->getOrInsertFunction(strprintf("llvm.memset.p0i8.i%d", fir::getNativeWordSizeInBits()), ft); + fn = module->getOrInsertFunction(strprintf("llvm.memset.p0i8.i%d", fir::getNativeWordSizeInBits()), ft).getCallee(); } else if(intr.first.str() == "memcmp") { diff --git a/source/include/backends/llvm.h b/source/include/backends/llvm.h index c905a00e..2bbac561 100644 --- a/source/include/backends/llvm.h +++ b/source/include/backends/llvm.h @@ -38,6 +38,8 @@ #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" +#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" +#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #ifdef _MSC_VER @@ -65,12 +67,10 @@ namespace backend struct LLVMJit { - // typedef llvm::orc::IRCompileLayer::ModuleHandleT ModuleHandle_t; - using ModuleHandle_t = llvm::orc::VModuleKey; + using OptimiseFunction = std::function(std::unique_ptr)>; - LLVMJit(llvm::TargetMachine* tm); - llvm::TargetMachine* getTargetMachine(); + LLVMJit(); void removeModule(ModuleHandle_t mod); ModuleHandle_t addModule(std::unique_ptr mod); @@ -79,13 +79,29 @@ namespace backend llvm::JITTargetAddress getSymbolAddress(const std::string& name); private: - llvm::orc::ExecutionSession execSession; - std::unique_ptr targetMachine; - std::shared_ptr symbolResolver; - llvm::DataLayout dataLayout; - llvm::orc::RTDyldObjectLinkingLayer objectLayer; - llvm::orc::IRCompileLayer compileLayer; + std::unique_ptr optimiseModule(std::unique_ptr M); + + llvm::orc::ExecutionSession ES; + std::map> Resolvers; + std::unique_ptr TM; + const llvm::DataLayout DL; + llvm::orc::LegacyRTDyldObjectLinkingLayer ObjectLayer; + llvm::orc::LegacyIRCompileLayer CompileLayer; + + llvm::orc::LegacyIRTransformLayer OptimiseLayer; + + std::unique_ptr CompileCallbackManager; + llvm::orc::LegacyCompileOnDemandLayer CODLayer; + + + // llvm::orc::ExecutionSession execSession; + // std::unique_ptr targetMachine; + // std::shared_ptr symbolResolver; + + // llvm::DataLayout dataLayout; + // llvm::orc::RTDyldObjectLinkingLayer objectLayer; + // llvm::orc::IRCompileLayer compileLayer; }; struct LLVMBackend : Backend From 9c74b0c53068d318c669c3add9b9754f54a5edd4 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 18 Oct 2019 23:30:21 +0800 Subject: [PATCH 026/129] fix jit on windows. --- build/run-test.ps1 | 2 +- meson.build | 14 ++- source/backend/llvm/jit.cpp | 129 +++++++++++-------------- source/backend/llvm/linker.cpp | 2 +- source/frontend/arguments.cpp | 2 +- source/frontend/parser/controlflow.cpp | 3 +- source/include/backends/llvm.h | 41 +++----- source/include/zpr.h | 91 +++++++++-------- 8 files changed, 130 insertions(+), 154 deletions(-) diff --git a/build/run-test.ps1 b/build/run-test.ps1 index dd703b91..bb5c2567 100644 --- a/build/run-test.ps1 +++ b/build/run-test.ps1 @@ -26,7 +26,7 @@ if($buildType -eq "debugopt") { ninja -C $buildDir if($?) { - cls + # cls & $buildDir\flaxc.exe -Ox -sysroot build\sysroot -run "build\$theProgram.flx" $extraArgs } diff --git a/meson.build b/meson.build index 9b4b62bb..9c84678b 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('flax', version: '0.40.0-pre', default_options: [ 'warning_level=3' ]) +project('flax', version: '0.41.7-pre', default_options: [ 'warning_level=3' ]) add_languages(['c', 'cpp']) @@ -59,7 +59,7 @@ if the_compiler.get_id() == 'msvc' envname_llvm = '%LLVM_ROOT_DIR%' envname_libffi = '%LIBFFI_ROOT_DIR%' - llvm_version = '7.1.0' + llvm_version = '9.0.0' mpir_root_dir = run_command('cmd.exe', '/C', 'echo', envname_mpir).stdout().strip() if mpir_root_dir == envname_mpir @@ -122,10 +122,12 @@ if the_compiler.get_id() == 'msvc' the_compiler.find_library('LLVMCodegen', dirs: llvm_lib_dir), the_compiler.find_library('LLVMX86Desc', dirs: llvm_lib_dir), the_compiler.find_library('LLVMSupport', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMRemarks', dirs: llvm_lib_dir), the_compiler.find_library('LLVMMCParser', dirs: llvm_lib_dir), the_compiler.find_library('LLVMX86Utils', dirs: llvm_lib_dir), the_compiler.find_library('LLVMAnalysis', dirs: llvm_lib_dir), the_compiler.find_library('LLVMTablegen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDemangle', dirs: llvm_lib_dir), the_compiler.find_library('LLVMVectorize', dirs: llvm_lib_dir), the_compiler.find_library('LLVMBitReader', dirs: llvm_lib_dir), the_compiler.find_library('LLVMLibDriver', dirs: llvm_lib_dir), @@ -140,11 +142,13 @@ if the_compiler.get_id() == 'msvc' the_compiler.find_library('LLVMSelectionDAG', dirs: llvm_lib_dir), the_compiler.find_library('LLVMBinaryFormat', dirs: llvm_lib_dir), the_compiler.find_library('LLVMX86AsmParser', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86AsmPrinter', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoPDB', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoDWARF', dirs: llvm_lib_dir), the_compiler.find_library('LLVMTransformUtils', dirs: llvm_lib_dir), the_compiler.find_library('LLVMMCDisassembler', dirs: llvm_lib_dir), the_compiler.find_library('LLVMExecutionEngine', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMDebugInfoCodeView', dirs: llvm_lib_dir) + the_compiler.find_library('LLVMBitstreamReader', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoCodeView', dirs: llvm_lib_dir), ] ) @@ -164,7 +168,7 @@ else # on unix, we use GMP instead of MPIR. mpfr_dep = dependency('mpfr', version: '>= 4.0.0') libffi_dep = dependency('libffi', version: '>= 3.2.1') - llvm_dep = dependency('llvm', static: true, version: '7.0.1', modules: [ + llvm_dep = dependency('llvm', static: true, version: '9.0.0', modules: [ 'core', 'engine', 'native', 'linker', 'bitwriter', 'lto', 'vectorize', 'all-targets', 'object', 'orcjit' ]) diff --git a/source/backend/llvm/jit.cpp b/source/backend/llvm/jit.cpp index ce7a0e28..495d3b2f 100644 --- a/source/backend/llvm/jit.cpp +++ b/source/backend/llvm/jit.cpp @@ -29,102 +29,91 @@ #endif +static std::string dealWithLLVMError(const llvm::Error& err) +{ + std::string str; + auto out = llvm::raw_string_ostream(str); + out << err; + return out.str(); +} namespace backend { - LLVMJit::LLVMJit() : - TM(llvm::EngineBuilder().selectTarget()), DL(TM->createDataLayout()), - ObjectLayer(llvm::AcknowledgeORCv1Deprecation, ES, [this](llvm::orc::VModuleKey K) -> auto { - return llvm::orc::LegacyRTDyldObjectLinkingLayer::Resources { - std::make_shared(), Resolvers[K] - }; - }), - CompileLayer(llvm::AcknowledgeORCv1Deprecation, ObjectLayer, llvm::orc::SimpleCompiler(*TM)), - OptimiseLayer(llvm::AcknowledgeORCv1Deprecation, CompileLayer, [this](std::unique_ptr M) -> auto { - return optimiseModule(std::move(M)); + LLVMJit::LLVMJit(llvm::orc::JITTargetMachineBuilder JTMB, llvm::DataLayout DL) : ObjectLayer(ES, []() { + return llvm::make_unique(); }), - CompileCallbackManager(cantFail(llvm::orc::createLocalCompileCallbackManager(TM->getTargetTriple(), ES, 0))), - CODLayer(llvm::AcknowledgeORCv1Deprecation, ES, OptimiseLayer, - [&](ModuleHandle_t K) { return Resolvers[K]; }, - [&](ModuleHandle_t K, std::shared_ptr R) { - Resolvers[K] = std::move(R); - }, - [](llvm::Function& F) { return std::set({ &F }); }, - *CompileCallbackManager, llvm::orc::createLocalIndirectStubsManagerBuilder(TM->getTargetTriple())) + CompileLayer(ES, ObjectLayer, llvm::orc::ConcurrentIRCompiler(std::move(JTMB))), + OptimiseLayer(ES, CompileLayer, optimiseModule), + DL(std::move(DL)), Mangle(ES, this->DL), + Ctx(llvm::make_unique()) { llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); + ES.getMainJITDylib().setGenerator(llvm::cantFail( + llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(DL.getGlobalPrefix()))); + + // dunno who's bright idea it was to match symbol flags *EXACTLY* instead of something more sane + ObjectLayer.setOverrideObjectFlagsWithResponsibilityFlags(true); + ObjectLayer.setAutoClaimResponsibilityForObjectSymbols(true); } - LLVMJit::ModuleHandle_t LLVMJit::addModule(std::unique_ptr mod) + void LLVMJit::addModule(std::unique_ptr mod) { - auto vmod = this->ES.allocateVModule(); - - this->Resolvers[vmod] = createLegacyLookupResolver(this->ES, [this](const std::string& name) -> llvm::JITSymbol { - if(auto Sym = CompileLayer.findSymbol(name, false)) - return Sym; - - else if(auto Err = Sym.takeError()) - return std::move(Err); - - if(auto SymAddr = llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name)) - return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported); - - return nullptr; - }, [](llvm::Error Err) { llvm::cantFail(std::move(Err), "lookupFlags failed"); }); - - // Add the module to the JIT with the new key. - llvm::cantFail(this->CODLayer.addModule(vmod, std::move(mod))); + // store it first lest it get stolen away + auto modIdent = mod->getModuleIdentifier(); - return vmod; + // llvm::Error::operator bool() returns true if there's an error. + if(auto err = OptimiseLayer.add(ES.getMainJITDylib(), llvm::orc::ThreadSafeModule(std::move(mod), Ctx)); err) + error("llvm: failed to add module '%s': %s", modIdent, dealWithLLVMError(err)); } - void LLVMJit::removeModule(LLVMJit::ModuleHandle_t mod) + llvm::JITEvaluatedSymbol LLVMJit::findSymbol(const std::string& name) { - llvm::cantFail(this->CODLayer.removeModule(mod)); + if(auto ret = ES.lookup({ &ES.getMainJITDylib() }, Mangle(name)); !ret) + error("llvm: failed to find symbol '%s': %s", name, dealWithLLVMError(ret.takeError())); + + else + return ret.get(); } - llvm::JITSymbol LLVMJit::findSymbol(const std::string& name) - { - std::string mangledName; - llvm::raw_string_ostream out(mangledName); - llvm::Mangler::getNameWithPrefix(out, name, this->DL); - return this->CODLayer.findSymbol(out.str(), false); - } - std::unique_ptr LLVMJit::optimiseModule(std::unique_ptr mod) + LLVMJit* LLVMJit::create() { - // Create a function pass manager. - auto fpm = llvm::make_unique(mod.get()); + auto JTMB = llvm::orc::JITTargetMachineBuilder::detectHost(); + if(!JTMB) error("llvm: failed to detect host", dealWithLLVMError(JTMB.takeError())); + + auto DL = JTMB->getDefaultDataLayoutForTarget(); + if(!DL) error("llvm: failed to get data layout", dealWithLLVMError(DL.takeError())); - // Add some optimisations. - fpm->add(llvm::createInstructionCombiningPass()); - fpm->add(llvm::createReassociatePass()); - fpm->add(llvm::createGVNPass()); - fpm->add(llvm::createCFGSimplificationPass()); - fpm->doInitialization(); + return new LLVMJit(std::move(*JTMB), std::move(*DL)); + } - // Run the optimizations over all functions in the module being added to the JIT. - for(auto& F : *mod) - fpm->run(F); - return mod; + llvm::Expected LLVMJit::optimiseModule(llvm::orc::ThreadSafeModule TSM, + const llvm::orc::MaterializationResponsibility& R) + { + // Create a function pass manager. + auto FPM = llvm::make_unique(TSM.getModule()); + + // Add some optimizations. + FPM->add(llvm::createInstructionCombiningPass()); + FPM->add(llvm::createReassociatePass()); + FPM->add(llvm::createGVNPass()); + FPM->add(llvm::createCFGSimplificationPass()); + FPM->doInitialization(); + + // Run the optimizations over all functions in the module being added to + // the JIT. + for(auto& F : *TSM.getModule()) + FPM->run(F); + + return TSM; } llvm::JITTargetAddress LLVMJit::getSymbolAddress(const std::string& name) { - auto addr = this->findSymbol(name).getAddress(); - if(!addr) - { - std::string err; - auto out = llvm::raw_string_ostream(err); - - out << addr.takeError(); - error("llvm: failed to find symbol '%s' (%s)", name, out.str()); - } - - return addr.get(); + return this->findSymbol(name).getAddress(); } } diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index 6c7c629a..2183455f 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -441,7 +441,7 @@ namespace backend { auto name = this->entryFunction->getName().str(); - this->jitInstance = new LLVMJit(); + this->jitInstance = LLVMJit::create(); this->jitInstance->addModule(std::move(this->linkedModule)); auto entryaddr = this->jitInstance->getSymbolAddress(name); diff --git a/source/frontend/arguments.cpp b/source/frontend/arguments.cpp index e1b08ea9..087477e7 100644 --- a/source/frontend/arguments.cpp +++ b/source/frontend/arguments.cpp @@ -8,7 +8,7 @@ #include -#define FLAX_VERSION_STRING "0.41.4-pre" +#define FLAX_VERSION_STRING "0.41.7-pre" #define ARG_COMPILE_ONLY "-c" #define ARG_BACKEND "-backend" diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index fdb1e1f7..2eeeeaf3 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -124,7 +124,8 @@ namespace parser for(auto& c : ret->cases) c.body->doNotPushNewScope = true; - ret->elseCase->doNotPushNewScope = true; + if(ret->elseCase) + ret->elseCase->doNotPushNewScope = true; return ret; } diff --git a/source/include/backends/llvm.h b/source/include/backends/llvm.h index 2bbac561..97a4d1f4 100644 --- a/source/include/backends/llvm.h +++ b/source/include/backends/llvm.h @@ -37,6 +37,7 @@ #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" @@ -67,41 +68,27 @@ namespace backend struct LLVMJit { - using ModuleHandle_t = llvm::orc::VModuleKey; using OptimiseFunction = std::function(std::unique_ptr)>; - LLVMJit(); - - void removeModule(ModuleHandle_t mod); - ModuleHandle_t addModule(std::unique_ptr mod); - - llvm::JITSymbol findSymbol(const std::string& name); + void addModule(std::unique_ptr mod); + llvm::JITEvaluatedSymbol findSymbol(const std::string& name); llvm::JITTargetAddress getSymbolAddress(const std::string& name); - private: + static LLVMJit* create(); - std::unique_ptr optimiseModule(std::unique_ptr M); + private: + LLVMJit(llvm::orc::JITTargetMachineBuilder JTMB, llvm::DataLayout DL); + static llvm::Expected optimiseModule(llvm::orc::ThreadSafeModule TSM, + const llvm::orc::MaterializationResponsibility& R); llvm::orc::ExecutionSession ES; - std::map> Resolvers; - std::unique_ptr TM; - const llvm::DataLayout DL; - llvm::orc::LegacyRTDyldObjectLinkingLayer ObjectLayer; - llvm::orc::LegacyIRCompileLayer CompileLayer; - - llvm::orc::LegacyIRTransformLayer OptimiseLayer; - - std::unique_ptr CompileCallbackManager; - llvm::orc::LegacyCompileOnDemandLayer CODLayer; - - - // llvm::orc::ExecutionSession execSession; - // std::unique_ptr targetMachine; - // std::shared_ptr symbolResolver; + llvm::orc::RTDyldObjectLinkingLayer ObjectLayer; + llvm::orc::IRCompileLayer CompileLayer; + llvm::orc::IRTransformLayer OptimiseLayer; - // llvm::DataLayout dataLayout; - // llvm::orc::RTDyldObjectLinkingLayer objectLayer; - // llvm::orc::IRCompileLayer compileLayer; + llvm::DataLayout DL; + llvm::orc::MangleAndInterner Mangle; + llvm::orc::ThreadSafeContext Ctx; }; struct LLVMBackend : Backend diff --git a/source/include/zpr.h b/source/include/zpr.h index 4dae857e..60989fd8 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -435,68 +435,63 @@ namespace zpr { std::string print(const T& x, const format_args& args) { - std::string num; - { - constexpr int default_prec = 6; + constexpr int default_prec = 6; + + char buf[81] = { 0 }; + int64_t num_length = 0; - char buf[81] = { 0 }; - int64_t num_length = 0; + // lmao. nobody except msvc stl (and only the most recent version) implements std::to_chars + // for floating point types, even though it's in the c++17 standard. so we just cheat. - // lmao. nobody except msvc stl (and only the most recent version) implements std::to_chars - // for floating point types, even though it's in the c++17 standard. so we just cheat. + // let printf handle the precision, but we'll handle the width and the negativity. + { + const char* fmt_str = 0; + bool longdouble = (args.length == format_args::LENGTH_LONG_DOUBLE); - // let printf handle the precision, but we'll handle the width and the negativity. + switch(args.specifier) { - const char* fmt_str = 0; - bool longdouble = (args.length == format_args::LENGTH_LONG_DOUBLE); - - switch(args.specifier) - { - case 'E': fmt_str = (longdouble ? "%.*LE" : "%.*E"); break; - case 'e': fmt_str = (longdouble ? "%.*Le" : "%.*e"); break; - case 'F': fmt_str = (longdouble ? "%.*LF" : "%.*F"); break; - case 'f': fmt_str = (longdouble ? "%.*Lf" : "%.*f"); break; - case 'G': fmt_str = (longdouble ? "%.*LG" : "%.*G"); break; - - case 'g': [[fallthrough]]; - default: fmt_str = (longdouble ? "%.*Lg" : "%.*g"); break; - } - - num_length = snprintf(&buf[0], 80, fmt_str, - (args.precision == -1 ? default_prec : args.precision), fabs(x)); + case 'E': fmt_str = (longdouble ? "%.*LE" : "%.*E"); break; + case 'e': fmt_str = (longdouble ? "%.*Le" : "%.*e"); break; + case 'F': fmt_str = (longdouble ? "%.*LF" : "%.*F"); break; + case 'f': fmt_str = (longdouble ? "%.*Lf" : "%.*f"); break; + case 'G': fmt_str = (longdouble ? "%.*LG" : "%.*G"); break; + + case 'g': [[fallthrough]]; + default: fmt_str = (longdouble ? "%.*Lg" : "%.*g"); break; } - auto abs_field_width = std::abs(args.width); + num_length = snprintf(&buf[0], 80, fmt_str, + (args.precision == -1 ? default_prec : args.precision), fabs(x)); + } - bool use_zero_pad = args.zero_pad && args.width >= 0; - bool use_left_pad = !use_zero_pad && args.width >= 0; - bool use_right_pad = !use_zero_pad && args.width < 0; + auto abs_field_width = std::abs(args.width); - // account for the signs, if any. - if(x < 0 || args.prepend_plus_if_positive || args.prepend_blank_if_positive) - num_length += 1; + bool use_zero_pad = args.zero_pad && args.width >= 0; + bool use_left_pad = !use_zero_pad && args.width >= 0; + bool use_right_pad = !use_zero_pad && args.width < 0; - std::string pre_prefix; - if(use_left_pad) - pre_prefix = std::string(std::max(int64_t(0), abs_field_width - num_length), ' '); + // account for the signs, if any. + if(x < 0 || args.prepend_plus_if_positive || args.prepend_blank_if_positive) + num_length += 1; - std::string prefix; - if(x < 0) prefix = "-"; - else if(args.prepend_plus_if_positive) prefix = "+"; - else if(args.prepend_blank_if_positive) prefix = " "; + std::string pre_prefix; + if(use_left_pad) + pre_prefix = std::string(std::max(int64_t(0), abs_field_width - num_length), ' '); - std::string post_prefix; - if(use_zero_pad) - post_prefix = std::string(std::max(int64_t(0), abs_field_width - num_length), '0'); + std::string prefix; + if(x < 0) prefix = "-"; + else if(args.prepend_plus_if_positive) prefix = "+"; + else if(args.prepend_blank_if_positive) prefix = " "; - std::string postfix; - if(use_right_pad) - postfix = std::string(std::max(int64_t(0), abs_field_width - num_length), ' '); + std::string post_prefix; + if(use_zero_pad) + post_prefix = std::string(std::max(int64_t(0), abs_field_width - num_length), '0'); - return pre_prefix + prefix + post_prefix + std::string(buf) + postfix; - } + std::string postfix; + if(use_right_pad) + postfix = std::string(std::max(int64_t(0), abs_field_width - num_length), ' '); - return num; + return pre_prefix + prefix + post_prefix + std::string(buf) + postfix; } }; From 5d35048939f72c138b8281b570b559538d6e5ea0 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 18 Oct 2019 23:43:23 +0800 Subject: [PATCH 027/129] update CI envs to use llvm 9 as well --- .semaphore/semaphore.yml | 12 ++++++------ .travis.yml | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 136c0a23..bd0f97f1 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -15,12 +15,12 @@ blocks: - name: build commands: - checkout - - sudo echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main" | sudo tee -a /etc/apt/sources.list - - sudo echo "deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main" | sudo tee -a /etc/apt/sources.list + - sudo echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list + - sudo echo "deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list - sudo wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo apt -y update - - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-7 llvm-7-dev libllvm7 libmpfr-dev libmpfr6 - - CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-7 make -j2 build - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx + - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 + - CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-9 make -j2 build + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile build/tester.flx && ./tester + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester diff --git a/.travis.yml b/.travis.yml index ce4d3d54..9ea5e0a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,42 +11,42 @@ matrix: cache: directories: - - llvm@7 + - llvm addons: apt: sources: - - llvm-toolchain-xenial-7 + - llvm-toolchain-xenial-9 - ubuntu-toolchain-r-test packages: - g++-9 - - llvm-7 - - llvm-7-dev - - libllvm7 + - llvm-9 + - llvm-9-dev + - libllvm9 - libmpfr-dev - libmpfr4 script: - | if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ ! -d "llvm@7/7.1.0/bin" ]; then - curl 'https://homebrew.bintray.com/bottles/llvm@7-7.1.0.high_sierra.bottle.tar.gz' -L -o llvm-7.1.0.tar.gz; - tar -xf llvm-7.1.0.tar.gz; + if [ ! -d "llvm/9.0.0/bin" ]; then + curl 'https://homebrew.bintray.com/bottles/llvm-9.0.0.high_sierra.bottle.tar.gz' -L -o llvm-9.0.0.tar.gz; + tar -xf llvm-9.0.0.tar.gz; fi; echo "clang version:" $($CC --version); echo "clang path:" $(which $CC); - PATH="$PATH:$(pwd)/llvm@7/7.1.0/bin" LLVM_CONFIG=llvm-config ENABLE_CODE_COVERAGE=yes make -j2 build; + PATH="$PATH:$(pwd)/llvm/9.0.0/bin" LLVM_CONFIG=llvm-config ENABLE_CODE_COVERAGE=yes make -j2 build; else - CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-7 ENABLE_CODE_COVERAGE=yes make -j2 build; + CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-9 ENABLE_CODE_COVERAGE=yes make -j2 build; fi - - LLVM_PROFILE_FILE="tester_llvm.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile -run -backend llvm build/tester.flx + - LLVM_PROFILE_FILE="tester_llvm.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend llvm build/tester.flx - LLVM_PROFILE_FILE="tester_interp.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx - - LLVM_PROFILE_FILE="tester_compile.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile build/tester.flx && ./tester + - LLVM_PROFILE_FILE="tester_compile.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile build/tester.flx && ./tester - | if [ "$TRAVIS_OS_NAME" == "osx" ]; then - llvm@7/7.1.0/bin/llvm-profdata merge -sparse tester_llvm.profraw tester_interp.profraw tester_compile.profraw -o tester_all.profdata; - llvm@7/7.1.0/bin/llvm-cov show ./build/sysroot/usr/local/bin/flaxc -instr-profile=tester_all.profdata > coverage.txt; + llvm/9.0.0/bin/llvm-profdata merge -sparse tester_llvm.profraw tester_interp.profraw tester_compile.profraw -o tester_all.profdata; + llvm/9.0.0/bin/llvm-cov show ./build/sysroot/usr/local/bin/flaxc -instr-profile=tester_all.profdata > coverage.txt; else for filename in `find . -name '*.cpp'`; do gcov-9 -o "$filename".gcno $filename > /dev/null; done fi From 2f69458095df47002273a6b877fa501d4d8fb494 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 18 Oct 2019 23:50:34 +0800 Subject: [PATCH 028/129] WILL THIS EMPTY THE CACHE? --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 3beda0d1..aaf3e580 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,6 +31,7 @@ install: # Install meson - cmd: pip install meson + build_script: - ps: meson --buildtype=release build/meson-rel - ps: ninja -C build/meson-rel From 159d7ce8ef94a8146fb9c9a49d66a7117849c016 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:12:06 +0800 Subject: [PATCH 029/129] hmm --- .semaphore/semaphore.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index bd0f97f1..07e87839 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -24,3 +24,18 @@ blocks: - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester + - name: "macos-build" + task: + agent: + machine: + type: a1-standard-4 + os_image: macos-mojave-xcode10 + jobs: + - name: build + commands: + - checkout + - HOMEBREW_NO_INSTALL_CLEANUP=1 brew install llvm mpfr libffi + - PATH="/usr/local/opt/llvm/bin:$PATH" LLVM_CONFIG=llvm-config make -j4 build + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester From a7598971e4624f197787466cdafe679de7ded0c3 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:14:08 +0800 Subject: [PATCH 030/129] oops --- .semaphore/semaphore.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 07e87839..d653d57f 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -25,17 +25,17 @@ blocks: - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester - name: "macos-build" - task: - agent: - machine: - type: a1-standard-4 - os_image: macos-mojave-xcode10 - jobs: - - name: build - commands: - - checkout - - HOMEBREW_NO_INSTALL_CLEANUP=1 brew install llvm mpfr libffi - - PATH="/usr/local/opt/llvm/bin:$PATH" LLVM_CONFIG=llvm-config make -j4 build - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester + task: + agent: + machine: + type: a1-standard-4 + os_image: macos-mojave-xcode10 + jobs: + - name: build + commands: + - checkout + - HOMEBREW_NO_INSTALL_CLEANUP=1 brew install llvm mpfr libffi + - PATH="/usr/local/opt/llvm/bin:$PATH" LLVM_CONFIG=llvm-config make -j4 build + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx + - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester From 2643bbef23dfa2acdda5504a5a5baf0cdffb845a Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:17:13 +0800 Subject: [PATCH 031/129] update readme? --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fa8338a7..9d9399b5 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,17 @@ A low level, general-purpose language with high level syntax and expressibility. [![forthebadge](https://forthebadge.com/images/badges/made-with-crayons.svg)](http://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](http://forthebadge.com) -[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) -     [![Build status](https://ci.appveyor.com/api/projects/status/c9cmm08t27ef1hji/branch/develop?svg=true)](https://ci.appveyor.com/project/zhiayang/flax/branch/develop)      -[![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) -     [![codecov](https://codecov.io/gh/flax-lang/flax/branch/develop/graph/badge.svg)](https://codecov.io/gh/flax-lang/flax)      [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) +
+Currently not expected to pass (for a while): +(Ubuntu 14.04 EOLed) [![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax)     +(llvm-toolchain-xenial-9 not approved on linux) [![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax)     + ----------------------------------------------- From f86c883c30149b54fe54460a0de7663e9bb2f3f2 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:17:58 +0800 Subject: [PATCH 032/129] update readme? (2) --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9d9399b5..e30ff8d9 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,13 @@ A low level, general-purpose language with high level syntax and expressibility.      [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) -
Currently not expected to pass (for a while): -(Ubuntu 14.04 EOLed) [![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax)     -(llvm-toolchain-xenial-9 not approved on linux) [![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax)     +(Ubuntu 14.04 EOLed) +[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) +     +(llvm-toolchain-xenial-9 not approved on linux) +[![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) +     From a82f57b29e56cdbb1c83c6d0580a4b1f3298a70a Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:18:50 +0800 Subject: [PATCH 033/129] update readme? (3) --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e30ff8d9..84262d85 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,11 @@ A low level, general-purpose language with high level syntax and expressibility.      [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) -Currently not expected to pass (for a while): -(Ubuntu 14.04 EOLed) +Currently not expected to pass (for a while):
[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) -     -(llvm-toolchain-xenial-9 not approved on linux) +— Ubuntu 14.04 EOLed [![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) -     +— llvm-toolchain-xenial-9 not approved on linux From b385ea82c5a05ceec96a547f49d597c9d2ff700e Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:19:15 +0800 Subject: [PATCH 034/129] update readme? (4) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84262d85..fce995c8 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ A low level, general-purpose language with high level syntax and expressibility. Currently not expected to pass (for a while):
[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) -— Ubuntu 14.04 EOLed +— Ubuntu 14.04 EOLed
[![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) — llvm-toolchain-xenial-9 not approved on linux From 0f93013700ac80640397e5ba19d819dcb852a1af Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 15:20:23 +0800 Subject: [PATCH 035/129] oh well --- .semaphore/semaphore.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index d653d57f..bd0f97f1 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -24,18 +24,3 @@ blocks: - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester - - name: "macos-build" - task: - agent: - machine: - type: a1-standard-4 - os_image: macos-mojave-xcode10 - jobs: - - name: build - commands: - - checkout - - HOMEBREW_NO_INSTALL_CLEANUP=1 brew install llvm mpfr libffi - - PATH="/usr/local/opt/llvm/bin:$PATH" LLVM_CONFIG=llvm-config make -j4 build - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester From 69f2ac6e0f0e87fae98e6d33b945fc08b7c28936 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 16:51:29 +0800 Subject: [PATCH 036/129] try and fix travis? --- .semaphore/semaphore.yml | 5 +- .travis.yml | 103 ++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index bd0f97f1..ee8bbf0f 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -15,9 +15,8 @@ blocks: - name: build commands: - checkout - - sudo echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list - - sudo echo "deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list - - sudo wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + - sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list + - sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo apt -y update - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 - CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-9 make -j2 build diff --git a/.travis.yml b/.travis.yml index 9ea5e0a3..bd9299cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,69 +2,70 @@ language: cpp compiler: clang matrix: - include: - - os: linux - dist: xenial - sudo: false - - os: osx - osx_image: xcode11.2 + include: + - os: linux + dist: xenial + sudo: false + - os: osx + osx_image: xcode11.2 cache: - directories: - - llvm + directories: + - llvm addons: - apt: - sources: - - llvm-toolchain-xenial-9 - - ubuntu-toolchain-r-test + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main + key_url: https://apt.llvm.org/llvm-snapshot.gpg.key - packages: - - g++-9 - - llvm-9 - - llvm-9-dev - - libllvm9 - - libmpfr-dev - - libmpfr4 + packages: + - g++-9 + - llvm-9 + - llvm-9-dev + - libllvm9 + - libmpfr-dev + - libmpfr4 script: - - | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ ! -d "llvm/9.0.0/bin" ]; then - curl 'https://homebrew.bintray.com/bottles/llvm-9.0.0.high_sierra.bottle.tar.gz' -L -o llvm-9.0.0.tar.gz; - tar -xf llvm-9.0.0.tar.gz; - fi; - echo "clang version:" $($CC --version); - echo "clang path:" $(which $CC); - PATH="$PATH:$(pwd)/llvm/9.0.0/bin" LLVM_CONFIG=llvm-config ENABLE_CODE_COVERAGE=yes make -j2 build; - else - CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-9 ENABLE_CODE_COVERAGE=yes make -j2 build; - fi - - LLVM_PROFILE_FILE="tester_llvm.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend llvm build/tester.flx - - LLVM_PROFILE_FILE="tester_interp.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx - - LLVM_PROFILE_FILE="tester_compile.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile build/tester.flx && ./tester - - | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - llvm/9.0.0/bin/llvm-profdata merge -sparse tester_llvm.profraw tester_interp.profraw tester_compile.profraw -o tester_all.profdata; - llvm/9.0.0/bin/llvm-cov show ./build/sysroot/usr/local/bin/flaxc -instr-profile=tester_all.profdata > coverage.txt; - else - for filename in `find . -name '*.cpp'`; do gcov-9 -o "$filename".gcno $filename > /dev/null; done - fi + - | + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + if [ ! -d "llvm/9.0.0/bin" ]; then + curl 'https://homebrew.bintray.com/bottles/llvm-9.0.0.high_sierra.bottle.tar.gz' -L -o llvm-9.0.0.tar.gz; + tar -xf llvm-9.0.0.tar.gz; + fi; + echo "clang version:" $($CC --version); + echo "clang path:" $(which $CC); + PATH="$PATH:$(pwd)/llvm/9.0.0/bin" LLVM_CONFIG=llvm-config ENABLE_CODE_COVERAGE=yes make -j2 build; + else + CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-9 ENABLE_CODE_COVERAGE=yes make -j2 build; + fi + - LLVM_PROFILE_FILE="tester_llvm.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend llvm build/tester.flx + - LLVM_PROFILE_FILE="tester_interp.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx + - LLVM_PROFILE_FILE="tester_compile.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile build/tester.flx && ./tester + - | + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + llvm/9.0.0/bin/llvm-profdata merge -sparse tester_llvm.profraw tester_interp.profraw tester_compile.profraw -o tester_all.profdata; + llvm/9.0.0/bin/llvm-cov show ./build/sysroot/usr/local/bin/flaxc -instr-profile=tester_all.profdata > coverage.txt; + else + for filename in `find . -name '*.cpp'`; do gcov-9 -o "$filename".gcno $filename > /dev/null; done + fi notifications: - email: false + email: false after_success: - - zip -r $TRAVIS_OS_NAME-x64 build/sysroot - - bash <(curl -s https://codecov.io/bash) + - zip -r $TRAVIS_OS_NAME-x64 build/sysroot + - bash <(curl -s https://codecov.io/bash) deploy: - provider: releases - api-key: ${GITHUB_OAUTH_TOKEN} - file: $TRAVIS_OS_NAME-x64.zip - skip-cleanup: true - on: - all_branches: true - tags: true + provider: releases + api-key: ${GITHUB_OAUTH_TOKEN} + file: $TRAVIS_OS_NAME-x64.zip + skip-cleanup: true + on: + all_branches: true + tags: true From 6769290477064af2d35ea05ee03dc02f01c9b1bc Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 19 Oct 2019 16:57:56 +0800 Subject: [PATCH 037/129] udpdate readme --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fce995c8..ad7ab2c8 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A low level, general-purpose language with high level syntax and expressibility. [![forthebadge](https://forthebadge.com/images/badges/made-with-crayons.svg)](http://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](http://forthebadge.com) +[![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) +     [![Build status](https://ci.appveyor.com/api/projects/status/c9cmm08t27ef1hji/branch/develop?svg=true)](https://ci.appveyor.com/project/zhiayang/flax/branch/develop)      [![codecov](https://codecov.io/gh/flax-lang/flax/branch/develop/graph/badge.svg)](https://codecov.io/gh/flax-lang/flax) @@ -14,10 +16,6 @@ A low level, general-purpose language with high level syntax and expressibility. Currently not expected to pass (for a while):
[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) -— Ubuntu 14.04 EOLed
-[![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) -— llvm-toolchain-xenial-9 not approved on linux - ----------------------------------------------- From cbaca9939fb009840d416b7e69cbb3a46fb9da8f Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sun, 20 Oct 2019 01:08:14 +0800 Subject: [PATCH 038/129] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ad7ab2c8..88d901e1 100644 --- a/README.md +++ b/README.md @@ -114,8 +114,10 @@ do { ### Building the Flax compiler +Note: the `master` branch is still using LLVM 7, and is stable; `develop` is now on LLVM 9. Changes will be merged into `master` eventually. + #### Dependencies #### -- LLVM 7, mostly due to their obsession with changing the IR interface every damn version +- LLVM 9, mostly due to their obsession with changing the IR interface every damn version - GMP/MPIR - MPFR - libffi @@ -124,7 +126,7 @@ do { #### macOS / Linux - The `makefile` is the preferred way to build on UNIX systems. -- LLVM needs to be installed. On macOS, `brew install llvm@7` should work, and you might need to do some PPA fiddling for Debian-based distros. +- LLVM needs to be installed. On macOS, `brew install llvm` should work, and you might need to do some PPA fiddling for Debian-based distros. - A C++17-compatible compiler should be used. - Find the `flaxc` executable in `build/sysroot/usr/local/bin`. - Additionally, the (admittedly limited) standard library will be copied from `./libs` to `./build/sysroot/usr/local/lib/flaxlibs/`. From 8402c46bec69d5d04f6ffbe1339a5b49ba307734 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 18:32:08 +0800 Subject: [PATCH 039/129] Add @packed attribute for structs. Closes #32. --- build/ultratiny.flx | 30 +++++++++++++++++----- source/fir/Types/Type.cpp | 44 +++++++++++++++++++++++---------- source/frontend/lexer.cpp | 6 +++++ source/frontend/parser/expr.cpp | 6 ++++- source/frontend/parser/misc.cpp | 15 ++++++++--- source/include/lexer.h | 1 + source/include/stcommon.h | 1 + source/include/utils.h | 12 +++++++++ source/typecheck/structs.cpp | 2 +- 9 files changed, 93 insertions(+), 24 deletions(-) diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 6af29bf9..909c3d87 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -4,9 +4,9 @@ export ultratiny -import libc as _ -import std::math - +// import libc as _ +// import std::math +/* @compiler_support["raii_trait::drop"] trait Drop { fn deinit() } @compiler_support["raii_trait::copy"] trait Copy { fn copy(other: &self) } @compiler_support["raii_trait::move"] trait Move { fn move(other: &mut self) } @@ -49,12 +49,30 @@ fn stuff2() -> Bar { return Bar(x: 57) } + */ + +ffi fn printf(fmt: &i8, ...) -> i32 + +struct Thicc +{ + x: i64 + y: i8 +} + +@packed struct Flat +{ + x: i64 + y: i8 +} @entry fn main() { - let q = stuff() - let p = stuff2() - printf("q = %d\n", q) + // let q = stuff() + // let p = stuff2() + // printf("q = %d\n", q) + let x = Flat(x: 3, y: 20) + + printf("%d, %d, %d\n", sizeof(x), sizeof(Thicc), sizeof(Flat)) } diff --git a/source/fir/Types/Type.cpp b/source/fir/Types/Type.cpp index 8a88f0f1..b0a491e5 100644 --- a/source/fir/Types/Type.cpp +++ b/source/fir/Types/Type.cpp @@ -1005,28 +1005,44 @@ namespace fir - static size_t getAggregateSize(const std::vector& tys) + static size_t getAggregateSize(const std::vector& tys, bool packed = false) { size_t ptr = 0; size_t aln = 0; - for(auto ty : tys) + if(packed) { - auto a = getAlignmentOfType(ty); - iceAssert(a > 0); + // gg + // return util::foldl(0, tys, [](Type* a, Type* b) -> size_t { + // return getSizeOfType(a) + getSizeOfType(b); + // }); - if(ptr % a > 0) - ptr += (a - (ptr % a)); + size_t ret = 0; + for(const auto& t : tys) + ret += getSizeOfType(t); - ptr += getSizeOfType(ty); - aln = std::max(aln, a); + return ret; } + else + { + for(auto ty : tys) + { + auto a = getAlignmentOfType(ty); + iceAssert(a > 0); - iceAssert(aln > 0); - if(ptr % aln > 0) - ptr += (aln - (ptr % aln)); + if(ptr % a > 0) + ptr += (a - (ptr % a)); - return ptr; + ptr += getSizeOfType(ty); + aln = std::max(aln, a); + } + + iceAssert(aln > 0); + if(ptr % aln > 0) + ptr += (aln - (ptr % aln)); + + return ptr; + } } size_t getSizeOfType(Type* type) @@ -1058,6 +1074,7 @@ namespace fir } else if(type->isClassType() || type->isStructType() || type->isTupleType()) { + bool packed = false; std::vector tys; if(type->isClassType()) @@ -1067,6 +1084,7 @@ namespace fir } else if(type->isStructType()) { + packed = type->toStructType()->isPackedStruct(); tys = type->toStructType()->getElements(); } else @@ -1074,7 +1092,7 @@ namespace fir tys = type->toTupleType()->getElements(); } - return getAggregateSize(tys); + return getAggregateSize(tys, packed); } else if(type->isUnionType() ) { diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index 9660b107..c5eebfe6 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -401,6 +401,12 @@ namespace lexer tok.text = "@entry"; read = 6; } + else if(hasPrefix(stream, "@packed")) + { + tok.type = TokenType::Attr_Packed; + tok.text = "@packed"; + read = 7; + } else if(hasPrefix(stream, "@raw")) { tok.type = TokenType::Attr_Raw; diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index b1597ba5..9e761845 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -113,6 +113,10 @@ namespace parser if((attrs.flags & FN_ENTRYPOINT) && !(allowed.flags & FN_ENTRYPOINT)) error(ret, "unsupported attribute '@entry' on %s", ret->readableName); + if((attrs.flags & PACKED) && !(allowed.flags & PACKED)) + error(ret, "unsupported attribute '@packed' on %s", ret->readableName); + + // here let's check the arguments and stuff for default attributes. // note: due to poor API design on my part, if there is no attribute with that name then ::get() // returns an empty UA, which has a blank name -- so we check that instead. @@ -155,7 +159,7 @@ namespace parser return enforceAttrs(parseUnion(st, attrs.has(attr::RAW), /* nameless: */ false), AttribSet::of(attr::RAW)); case TT::Struct: - return enforceAttrs(parseStruct(st, /* nameless: */ false)); + return enforceAttrs(parseStruct(st, /* nameless: */ false), AttribSet::of(attr::PACKED)); case TT::Class: return enforceAttrs(parseClass(st)); diff --git a/source/frontend/parser/misc.cpp b/source/frontend/parser/misc.cpp index bbf10265..bc74bab0 100644 --- a/source/frontend/parser/misc.cpp +++ b/source/frontend/parser/misc.cpp @@ -104,11 +104,14 @@ namespace parser { using UA = AttribSet::UserAttrib; - if(st.front() <= TT::Attr_ATTRS_BEGIN || st.front() >= TT::Attr_ATTRS_END) + if(st.front() != TT::At && (st.front() <= TT::Attr_ATTRS_BEGIN || st.front() >= TT::Attr_ATTRS_END)) return AttribSet::of(attr::NONE); auto parseUA = [](State& st) -> UA { + iceAssert(st.front() == TT::At); + st.pop(); + auto ret = UA(st.eat().str(), {}); if(st.front() == TT::LSquare) @@ -142,23 +145,29 @@ namespace parser AttribSet ret; - while(st.front() > TT::Attr_ATTRS_BEGIN && st.front() < TT::Attr_ATTRS_END) + while(true) { // i would love me some static reflection right now switch(st.front()) { case TT::Attr_Raw: ret.set(attr::RAW); st.pop(); break; + case TT::Attr_Packed: ret.set(attr::PACKED); st.pop(); break; case TT::Attr_NoMangle: ret.set(attr::NO_MANGLE); st.pop(); break; case TT::Attr_EntryFn: ret.set(attr::FN_ENTRYPOINT); st.pop(); break; case TT::Attr_Platform: unexpected(st.loc(), "@platform definition"); case TT::Attr_Operator: unexpected(st.loc(), "@operator declaration"); - default: + case TT::At: ret.add(parseUA(st)); break; + + default: + goto out; } } + // sue me + out: return ret; } diff --git a/source/include/lexer.h b/source/include/lexer.h index a942f66c..4b0fb0d9 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -121,6 +121,7 @@ namespace lexer Attr_ATTRS_BEGIN, Attr_Raw, + Attr_Packed, Attr_EntryFn, Attr_NoMangle, Attr_Operator, diff --git a/source/include/stcommon.h b/source/include/stcommon.h index d43698f5..14128dfc 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -23,6 +23,7 @@ namespace attr constexpr FlagTy FN_ENTRYPOINT = 0x1; constexpr FlagTy NO_MANGLE = 0x2; constexpr FlagTy RAW = 0x4; + constexpr FlagTy PACKED = 0x8; } struct AttribSet diff --git a/source/include/utils.h b/source/include/utils.h index 93c9382b..2a0d7b57 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -83,6 +83,18 @@ namespace util } + template + U foldl(const U& i, const std::vector& xs, FoldOp fn) + { + auto ret = i; + for(const auto& x : xs) + ret = fn(ret, x); + + return ret; + } + + + template ::type> std::vector map(const std::vector& input, UnaryOp fn) { diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index b8213ddb..d2fc73be 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -147,7 +147,7 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type for(auto m : this->methods) m->parentType = this, m->realScope = this->realScope + defn->id.name; - auto str = fir::StructType::createWithoutBody(defn->id); + auto str = fir::StructType::createWithoutBody(defn->id, /* isPacked: */ this->attrs.has(attr::PACKED)); defn->type = str; fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); From 823e482be387d72d9c6c538dd97a1fdf2e4e51aa Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 21:44:39 +0800 Subject: [PATCH 040/129] rename fir::ConstantString to ConstantCharSlice --- build/run-test.ps1 | 2 +- source/backend/llvm/translator.cpp | 2 +- source/codegen/alloc.cpp | 4 ++-- source/codegen/arithmetic.cpp | 16 ++++++++-------- source/codegen/builtin.cpp | 2 +- source/codegen/codegenstate.cpp | 2 +- source/codegen/destructure.cpp | 4 ++-- source/codegen/enums.cpp | 2 +- source/codegen/glue/any.cpp | 5 ++++- source/codegen/glue/saa_common.cpp | 2 +- source/codegen/ranges.cpp | 2 +- source/codegen/slice.cpp | 8 ++++---- source/codegen/subscript.cpp | 2 +- source/fir/ConstantValue.cpp | 8 ++++---- source/fir/interp/interpreter.cpp | 2 +- source/include/gluecode.h | 2 +- source/include/ir/constant.h | 6 +++--- 17 files changed, 37 insertions(+), 34 deletions(-) diff --git a/build/run-test.ps1 b/build/run-test.ps1 index bb5c2567..9178774b 100644 --- a/build/run-test.ps1 +++ b/build/run-test.ps1 @@ -27,7 +27,7 @@ ninja -C $buildDir if($?) { # cls - & $buildDir\flaxc.exe -Ox -sysroot build\sysroot -run "build\$theProgram.flx" $extraArgs + & $buildDir\flaxc.exe -Ox -sysroot build\sysroot -run "build\$theProgram.flx" --ffi-escape $extraArgs } if($buildType -eq "release") { diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 23fd6751..094a2860 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -457,7 +457,7 @@ namespace backend return cachedConstants[fc] = llvm::ConstantStruct::get(llvm::cast(ty), constToLlvm(cec->getIndex(), valueMap, mod), constToLlvm(cec->getValue(), valueMap, mod)); } - else if(auto cs = dcast(fir::ConstantString, fc)) + else if(auto cs = dcast(fir::ConstantCharSlice, fc)) { size_t origLen = cs->getValue().length(); std::string str = cs->getValue(); diff --git a/source/codegen/alloc.cpp b/source/codegen/alloc.cpp index 2f261934..801380ca 100644 --- a/source/codegen/alloc.cpp +++ b/source/codegen/alloc.cpp @@ -132,7 +132,7 @@ static fir::Value* performAllocation(cgn::CodegenState* cs, sst::AllocOp* alloc, //* if we don't have a count, then we just return a T* -- no arrays, nothing. auto sz = cs->irb.Multiply(cs->irb.Sizeof(type), cnt); - auto mem = cs->irb.Call(cgn::glue::misc::getMallocWrapperFunction(cs), sz, fir::ConstantString::get(alloc->loc.shortString())); + auto mem = cs->irb.Call(cgn::glue::misc::getMallocWrapperFunction(cs), sz, fir::ConstantCharSlice::get(alloc->loc.shortString())); mem = cs->irb.PointerTypeCast(mem, type->getMutablePointerTo()); callSetFunction(type, alloc, mem, cnt); @@ -154,7 +154,7 @@ static fir::Value* performAllocation(cgn::CodegenState* cs, sst::AllocOp* alloc, // make sure the length isn't negative auto checkf = getCheckNegativeLengthFunction(cs); iceAssert(checkf); - cs->irb.Call(checkf, count, fir::ConstantString::get(ecount->loc.toString())); + cs->irb.Call(checkf, count, fir::ConstantCharSlice::get(ecount->loc.toString())); auto arr = cs->irb.CreateValue(fir::DynamicArrayType::get(type)); auto expandfn = cgn::glue::saa_common::generateReserveAtLeastFunction(cs, arr->getType()); diff --git a/source/codegen/arithmetic.cpp b/source/codegen/arithmetic.cpp index c0d9e9b4..83e79c2a 100644 --- a/source/codegen/arithmetic.cpp +++ b/source/codegen/arithmetic.cpp @@ -66,7 +66,7 @@ namespace sst { // TODO: actually say what the variant was -- requires creating a runtime array of the names of the variants, // TODO: probably. might be easier once we have type info at runtime! - cgn::glue::printRuntimeError(cs, fir::ConstantString::get(cs->loc().toString()), + cgn::glue::printRuntimeError(cs, fir::ConstantCharSlice::get(cs->loc().toString()), "invalid unwrap of value of union '%s' into variant '%s'", { cs->module->createGlobalString(vt->str()), cs->module->createGlobalString(target->toUnionVariantType()->getName()) @@ -431,13 +431,13 @@ namespace cgn #if 0 // ok. // if we're both string literals, then fuck it, do it compile-time - if(dcast(fir::ConstantString, lv) && dcast(fir::ConstantString, rv)) + if(dcast(fir::ConstantCharSlice, lv) && dcast(fir::ConstantCharSlice, rv)) { - std::string cls = dcast(fir::ConstantString, lv)->getValue(); - std::string crs = dcast(fir::ConstantString, rv)->getValue(); + std::string cls = dcast(fir::ConstantCharSlice, lv)->getValue(); + std::string crs = dcast(fir::ConstantCharSlice, rv)->getValue(); info(loc, "const strings"); - return CGResult(fir::ConstantString::get(cls + crs)); + return CGResult(fir::ConstantCharSlice::get(cls + crs)); } #endif @@ -480,13 +480,13 @@ namespace cgn #if 0 - if(dcast(fir::ConstantString, lv) && dcast(fir::ConstantChar, rv)) + if(dcast(fir::ConstantCharSlice, lv) && dcast(fir::ConstantChar, rv)) { - std::string cls = dcast(fir::ConstantString, lv)->getValue(); + std::string cls = dcast(fir::ConstantCharSlice, lv)->getValue(); char crs = dcast(fir::ConstantChar, rv)->getValue(); info(loc, "const strings"); - return CGResult(fir::ConstantString::get(cls + crs)); + return CGResult(fir::ConstantCharSlice::get(cls + crs)); } #endif diff --git a/source/codegen/builtin.cpp b/source/codegen/builtin.cpp index bbd01758..160794fc 100644 --- a/source/codegen/builtin.cpp +++ b/source/codegen/builtin.cpp @@ -71,7 +71,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) error(this->lhs, "cannot call 'pop()' on an array type ('%s')", ty); auto popf = cgn::glue::array::getPopElementFromBackFunction(cs, ty); - auto tupl = cs->irb.Call(popf, res.value, fir::ConstantString::get(this->loc.toString())); + auto tupl = cs->irb.Call(popf, res.value, fir::ConstantCharSlice::get(this->loc.toString())); // tupl[0] is the new array // tupl[1] is the last element diff --git a/source/codegen/codegenstate.cpp b/source/codegen/codegenstate.cpp index c03df82e..2802c06f 100644 --- a/source/codegen/codegenstate.cpp +++ b/source/codegen/codegenstate.cpp @@ -180,7 +180,7 @@ namespace cgn { fir::Value* arr = this->irb.CreateValue(type); - arr = this->irb.SetSAAData(arr, this->irb.PointerTypeCast(this->irb.GetArraySliceData(fir::ConstantString::get("")), + arr = this->irb.SetSAAData(arr, this->irb.PointerTypeCast(this->irb.GetArraySliceData(fir::ConstantCharSlice::get("")), fir::Type::getMutInt8Ptr())); arr = this->irb.SetSAALength(arr, fir::ConstantInt::getNative(0)); arr = this->irb.SetSAACapacity(arr, fir::ConstantInt::getNative(0)); diff --git a/source/codegen/destructure.cpp b/source/codegen/destructure.cpp index 15fba661..e1313a67 100644 --- a/source/codegen/destructure.cpp +++ b/source/codegen/destructure.cpp @@ -94,7 +94,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul auto checkf = cgn::glue::string::getBoundsCheckFunction(cs, true); if(checkf) { - auto strloc = fir::ConstantString::get(bind.loc.toString()); + auto strloc = fir::ConstantCharSlice::get(bind.loc.toString()); cs->irb.Call(checkf, cs->irb.GetSAALength(rhs.value), numbinds, strloc); } } @@ -155,7 +155,7 @@ static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResul auto checkf = cgn::glue::array::getBoundsCheckFunction(cs, true); if(checkf) { - auto strloc = fir::ConstantString::get(bind.loc.toString()); + auto strloc = fir::ConstantCharSlice::get(bind.loc.toString()); cs->irb.Call(checkf, arrlen, numbinds, strloc); } } diff --git a/source/codegen/enums.cpp b/source/codegen/enums.cpp index 28e3441e..da051de7 100644 --- a/source/codegen/enums.cpp +++ b/source/codegen/enums.cpp @@ -28,7 +28,7 @@ CGResult sst::EnumDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) iceAssert(cv); // values[c->index] = fir::ConstantStruct(cv); - names[c->index] = fir::ConstantString::get(n); + names[c->index] = fir::ConstantCharSlice::get(n); } diff --git a/source/codegen/glue/any.cpp b/source/codegen/glue/any.cpp index 0ac45896..3722676c 100644 --- a/source/codegen/glue/any.cpp +++ b/source/codegen/glue/any.cpp @@ -218,9 +218,12 @@ namespace any auto arr = cs->irb.ReadPtr(arrptr); any = cs->irb.SetAnyData(any, arr); + + } any = cs->irb.SetAnyTypeID(any, fir::ConstantInt::getUNative(tid)); + cs->irb.Return(any); cs->irb.setCurrentBlock(restore); @@ -261,7 +264,7 @@ namespace any cs->irb.setCurrentBlock(invalid); { - printRuntimeError(cs, fir::ConstantString::get(cs->loc().toString()), + printRuntimeError(cs, fir::ConstantCharSlice::get(cs->loc().toString()), "invalid unwrap of 'any' with type id '%ld' into type '%s' (with id '%ld')", { tid, cs->module->createGlobalString(type->str()), fir::ConstantInt::getUNative(type->getID()) } ); diff --git a/source/codegen/glue/saa_common.cpp b/source/codegen/glue/saa_common.cpp index da060476..573bffb9 100644 --- a/source/codegen/glue/saa_common.cpp +++ b/source/codegen/glue/saa_common.cpp @@ -283,7 +283,7 @@ namespace saa_common auto newbytecount = cs->irb.Multiply(newcap, cs->irb.Sizeof(slicetype->getArrayElementType()), "newbytecount"); fir::Value* newbuf = cs->irb.Call(cgn::glue::misc::getMallocWrapperFunction(cs), - !isArray ? cs->irb.Add(newbytecount, getCI(1)) : newbytecount, fir::ConstantString::get("(no location)"), "buf"); + !isArray ? cs->irb.Add(newbytecount, getCI(1)) : newbytecount, fir::ConstantCharSlice::get("(no location)"), "buf"); { // fir::Function* memcpyf = cs->module->getIntrinsicFunction("memmove"); // cs->irb.Call(memcpyf, { buf, castRawBufToElmPtr(cs, saa, lhsbuf), lhsbytecount, diff --git a/source/codegen/ranges.cpp b/source/codegen/ranges.cpp index 9be74c13..3d331485 100644 --- a/source/codegen/ranges.cpp +++ b/source/codegen/ranges.cpp @@ -44,7 +44,7 @@ CGResult sst::RangeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) // now that we have all the values, it's time to sanity check these things. auto checkf = cgn::glue::misc::getRangeSanityCheckFunction(cs); - if(checkf) cs->irb.Call(checkf, ret, fir::ConstantString::get(this->loc.toString())); + if(checkf) cs->irb.Call(checkf, ret, fir::ConstantCharSlice::get(this->loc.toString())); return CGResult(ret); diff --git a/source/codegen/slice.cpp b/source/codegen/slice.cpp index a3e5ec5b..fb4231a5 100644 --- a/source/codegen/slice.cpp +++ b/source/codegen/slice.cpp @@ -51,15 +51,15 @@ static void checkSliceOperation(cgn::CodegenState* cs, sst::Expr* user, fir::Val cs->irb.setCurrentBlock(neg_begin); - cgn::glue::printRuntimeError(cs, fir::ConstantString::get(apos.toString()), + cgn::glue::printRuntimeError(cs, fir::ConstantCharSlice::get(apos.toString()), "slice start index '%ld' is < 0\n", { beginIndex }); cs->irb.setCurrentBlock(neg_end); - cgn::glue::printRuntimeError(cs, fir::ConstantString::get(bpos.toString()), + cgn::glue::printRuntimeError(cs, fir::ConstantCharSlice::get(bpos.toString()), "slice end index '%ld' is < 0\n", { endIndex }); cs->irb.setCurrentBlock(neg_len); - cgn::glue::printRuntimeError(cs, fir::ConstantString::get(bpos.toString()), + cgn::glue::printRuntimeError(cs, fir::ConstantCharSlice::get(bpos.toString()), "slice end index '%ld' is < start index '%ld'\n", { endIndex, beginIndex }); @@ -71,7 +71,7 @@ static void checkSliceOperation(cgn::CodegenState* cs, sst::Expr* user, fir::Val // of indices here. fir::Function* checkf = cgn::glue::array::getBoundsCheckFunction(cs, /* isPerformingDecomposition: */ true); if(checkf) - cs->irb.Call(checkf, maxlen, endIndex, fir::ConstantString::get(apos.toString())); + cs->irb.Call(checkf, maxlen, endIndex, fir::ConstantCharSlice::get(apos.toString())); } } diff --git a/source/codegen/subscript.cpp b/source/codegen/subscript.cpp index 0ca7238b..9d987296 100644 --- a/source/codegen/subscript.cpp +++ b/source/codegen/subscript.cpp @@ -73,7 +73,7 @@ CGResult sst::SubscriptOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) { fir::Function* checkf = cgn::glue::saa_common::generateBoundsCheckFunction(cs, /* isString: */ lt->isStringType(), /* isDecomp: */false); if(checkf) - cs->irb.Call(checkf, maxlength, index, fir::ConstantString::get(this->loc.shortString())); + cs->irb.Call(checkf, maxlength, index, fir::ConstantCharSlice::get(this->loc.shortString())); } // ok, do it diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 893ad7cf..13305ce1 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -243,17 +243,17 @@ namespace fir - ConstantString* ConstantString::get(std::string s) + ConstantCharSlice* ConstantCharSlice::get(std::string s) { - return new ConstantString(s); + return new ConstantCharSlice(s); } - ConstantString::ConstantString(std::string s) : ConstantValue(fir::Type::getCharSlice(false)) + ConstantCharSlice::ConstantCharSlice(std::string s) : ConstantValue(fir::Type::getCharSlice(false)) { this->str = s; } - std::string ConstantString::getValue() + std::string ConstantCharSlice::getValue() { return this->str; } diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 0aa90e3a..f4988670 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -273,7 +273,7 @@ namespace interp { return cachedConstants[c] = makeValue(c, cb->getValue()); } - else if(auto cs = dcast(fir::ConstantString, c)) + else if(auto cs = dcast(fir::ConstantCharSlice, c)) { auto str = cs->getValue(); diff --git a/source/include/gluecode.h b/source/include/gluecode.h index 566f8edf..ae41ffbe 100644 --- a/source/include/gluecode.h +++ b/source/include/gluecode.h @@ -14,7 +14,7 @@ #define DEBUG_RUNTIME_GLUE_MASTER 0 -#define DEBUG_STRING_MASTER (1 & DEBUG_RUNTIME_GLUE_MASTER) +#define DEBUG_STRING_MASTER (0 & DEBUG_RUNTIME_GLUE_MASTER) #define DEBUG_STRING_ALLOCATION (1 & DEBUG_STRING_MASTER) #define DEBUG_STRING_REFCOUNTING (1 & DEBUG_STRING_MASTER) diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 5a6c4be3..9cea6cfe 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -157,15 +157,15 @@ namespace fir ConstantValue* value = 0; }; - struct ConstantString : ConstantValue + struct ConstantCharSlice : ConstantValue { friend struct Module; - static ConstantString* get(std::string value); + static ConstantCharSlice* get(std::string value); std::string getValue(); protected: - ConstantString(std::string str); + ConstantCharSlice(std::string str); std::string str; }; From 418f67eed15cf9f294bcf396b6b7268c2ab6ae80 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 21:51:03 +0800 Subject: [PATCH 041/129] fix some oversights and other stuff 1. throw a proper error when tuple-assigning to a constant 2. make `.ptr` work on arrays 3. fix the `.names` field on enumerations 4. fix appending to literal arrays (we would previously call `realloc` on non-heap memory) 5. fix a very stupid mistake in the interpreter re: `str`s 6. fix some omissions in the typechecking part of operators --- libs/std/io.flx | 5 ++ source/backend/llvm/jit.cpp | 2 + source/codegen/assign.cpp | 3 + source/codegen/builtin.cpp | 22 ++++- source/codegen/glue/any.cpp | 4 +- source/codegen/glue/saa_common.cpp | 38 +++++--- source/codegen/literals.cpp | 137 +++++++++++++++-------------- source/codegen/subscript.cpp | 3 +- source/fir/IRBuilder.cpp | 5 ++ source/fir/interp/interpreter.cpp | 13 ++- source/include/ir/irbuilder.h | 1 + source/typecheck/arithmetic.cpp | 5 +- source/typecheck/dotop.cpp | 12 ++- 13 files changed, 157 insertions(+), 93 deletions(-) diff --git a/libs/std/io.flx b/libs/std/io.flx index 1a0317e3..04829967 100644 --- a/libs/std/io.flx +++ b/libs/std/io.flx @@ -108,6 +108,11 @@ public fn format(fmt: str, args: [any: ...]) -> string return ret } +// in case you're lazy +public fn println() +{ + libc::puts("") +} public fn println(fmt: str, args: [any: ...]) { diff --git a/source/backend/llvm/jit.cpp b/source/backend/llvm/jit.cpp index 495d3b2f..e2003b89 100644 --- a/source/backend/llvm/jit.cpp +++ b/source/backend/llvm/jit.cpp @@ -93,6 +93,7 @@ namespace backend llvm::Expected LLVMJit::optimiseModule(llvm::orc::ThreadSafeModule TSM, const llvm::orc::MaterializationResponsibility& R) { + #if 0 // Create a function pass manager. auto FPM = llvm::make_unique(TSM.getModule()); @@ -107,6 +108,7 @@ namespace backend // the JIT. for(auto& F : *TSM.getModule()) FPM->run(F); + #endif return TSM; } diff --git a/source/codegen/assign.cpp b/source/codegen/assign.cpp index c4d29ed5..56790c24 100644 --- a/source/codegen/assign.cpp +++ b/source/codegen/assign.cpp @@ -150,6 +150,9 @@ CGResult sst::TupleAssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(!res->islvalue()) error(v, "cannot assign to non-lvalue expression in tuple assignment"); + if(res->isConst()) + error(v, "cannot assign to constant in tuple assignment"); + results.push_back(res); idx++; } diff --git a/source/codegen/builtin.cpp b/source/codegen/builtin.cpp index 160794fc..2ad06d54 100644 --- a/source/codegen/builtin.cpp +++ b/source/codegen/builtin.cpp @@ -67,6 +67,9 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(!res->islvalue()) error(this->lhs, "cannot call 'pop()' on an rvalue"); + else if(res->isConst()) + error(this->lhs, "cannot call 'pop()' (which mutates) on a constant value"); + else if(ty->isArrayType()) error(this->lhs, "cannot call 'pop()' on an array type ('%s')", ty); @@ -148,8 +151,16 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) } else if(this->name == names::saa::FIELD_POINTER) { - auto ret = cs->irb.ConstGEP2(res.value, 0, 0); - return CGResult(ret); + // TODO: LVALUE HOLE + if(res.value->islvalue()) + { + auto ret = cs->irb.ConstGEP2(cs->irb.AddressOf(res.value, /* mut: */ false), 0, 0); + return CGResult(ret); + } + else + { + error("NOT SUP"); + } } } else if(ty->isRangeType()) @@ -185,10 +196,13 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) else if(this->name == names::enumeration::FIELD_NAME) { auto namearr = ty->toEnumType()->getNameArray(); - iceAssert(namearr->getType()->isPointerType() && namearr->getType()->getPointerElementType()->isArrayType()); + iceAssert(namearr->islvalue()); + + auto namearrptr = cs->irb.AddressOf(namearr, /* mut: */ false); + iceAssert(namearrptr->getType()->isPointerType() && namearrptr->getType()->getPointerElementType()->isArrayType()); auto idx = cs->irb.GetEnumCaseIndex(res.value); - auto n = cs->irb.GEP2(namearr, fir::ConstantInt::getNative(0), idx); + auto n = cs->irb.GEP2(namearrptr, fir::ConstantInt::getNative(0), idx); return CGResult(cs->irb.ReadPtr(n)); } diff --git a/source/codegen/glue/any.cpp b/source/codegen/glue/any.cpp index 3722676c..37b07059 100644 --- a/source/codegen/glue/any.cpp +++ b/source/codegen/glue/any.cpp @@ -292,7 +292,9 @@ namespace any // same as above but we skip a load. auto fakeptr = cs->irb.PointerTypeCast(arrptr, type->getMutablePointerTo()); - cs->irb.Return(cs->irb.ReadPtr(fakeptr)); + auto ret = cs->irb.ReadPtr(fakeptr); + + cs->irb.Return(ret); } } diff --git a/source/codegen/glue/saa_common.cpp b/source/codegen/glue/saa_common.cpp index 573bffb9..ea912558 100644 --- a/source/codegen/glue/saa_common.cpp +++ b/source/codegen/glue/saa_common.cpp @@ -578,7 +578,7 @@ namespace saa_common fir::Value* lhs = func->getArguments()[0]; fir::Value* rhs = func->getArguments()[1]; - auto rhsslice = cs->irb.CreateValue(getSAASlice(saa), "rhsslice"); + auto rhsslice = cs->irb.CreateValue(getSAASlice(saa, /* mut: */ false), "rhsslice"); rhsslice = cs->irb.SetArraySliceData(rhsslice, cs->irb.ImmutStackAlloc(getSAAElm(saa), rhs, "rhsptr")); rhsslice = cs->irb.SetArraySliceLength(rhsslice, getCI(1)); @@ -603,7 +603,7 @@ namespace saa_common - + // TODO: this shit is bloody unmaintainable fir::Function* generateReserveAtLeastFunction(CodegenState* cs, fir::Type* saa) { auto fname = misc::getReserveEnough_FName(saa); @@ -672,27 +672,45 @@ namespace saa_common auto newlen = cs->irb.Divide(cs->irb.Multiply(minsz, getCI(3)), getCI(2), "mul1.5"); // call realloc. handles the null case as well, which is nice. - auto oldbuf = cs->irb.PointerTypeCast(cs->irb.GetSAAData(s1), fir::Type::getMutInt8Ptr(), "oldbuf"); + const auto oldbuf = cs->irb.PointerTypeCast(cs->irb.GetSAAData(s1), fir::Type::getMutInt8Ptr(), "oldbuf"); auto newbytecount = cs->irb.Multiply(newlen, cs->irb.Sizeof(getSAAElm(saa)), "newbytecount"); if(saa->isStringType()) newbytecount = cs->irb.Add(newbytecount, getCI(1)); - // for "default" or empty strings, the buffer points to constant memory that did not come from the heap!! - // so, we cannot call realloc with oldbuf, and call it with NULL instead. we do this if the capacity was 0! + // if the capacity was negative, then the buffer points to constant memory that did not come from the heap!! + // so, we cannot call realloc with oldbuf, and call it with NULL instead. also for strings, capacity of 0 is empty + // so we do the same dealio. + auto isfake = cs->irb.ICmpLEQ(oldcap, getCI(0)); + auto origbuf = cs->irb.Select(isfake, fir::ConstantValue::getZeroValue(fir::Type::getMutInt8Ptr()), oldbuf); + + auto _newbuf = cs->irb.Call(cs->getOrDeclareLibCFunction(REALLOCATE_MEMORY_FUNC), origbuf, newbytecount, "newbuf"); + auto newbuf = castRawBufToElmPtr(cs, saa, _newbuf); + + + // again with the NULL thingy: now we need to (possibly) copy the data from the old buffer to the new buffer + // bopian need some branching here. { - auto isfake = cs->irb.ICmpEQ(oldcap, getCI(0)); - oldbuf = cs->irb.Select(isfake, fir::ConstantValue::getZeroValue(fir::Type::getMutInt8Ptr()), oldbuf); + auto needcopy = cs->irb.addNewBlockInFunction("copyold", func); + auto nocopy = cs->irb.addNewBlockInFunction("nocopyold", func); + + cs->irb.CondBranch(isfake, needcopy, nocopy); + cs->irb.setCurrentBlock(needcopy); + { + cs->irb.Call(cs->module->getIntrinsicFunction("memmove"), _newbuf, oldbuf, cs->irb.Multiply(oldlen, + cs->irb.Sizeof(getSAAElm(saa))), /* isVolatile: */ fir::ConstantBool::get(false)); + cs->irb.UnCondBranch(nocopy); + } + + cs->irb.setCurrentBlock(nocopy); } - auto newbuf = cs->irb.Call(cs->getOrDeclareLibCFunction(REALLOCATE_MEMORY_FUNC), oldbuf, newbytecount, "newbuf"); - newbuf = castRawBufToElmPtr(cs, saa, newbuf); - // null terminator if(saa->isStringType()) cs->irb.WritePtr(fir::ConstantInt::getInt8(0), cs->irb.GetPointer(newbuf, cs->irb.Subtract(newbytecount, getCI(1)))); + auto ret = cs->irb.CreateValue(saa); ret = cs->irb.SetSAAData(ret, newbuf); ret = cs->irb.SetSAALength(ret, oldlen); diff --git a/source/codegen/literals.cpp b/source/codegen/literals.cpp index 571914f1..0169523e 100644 --- a/source/codegen/literals.cpp +++ b/source/codegen/literals.cpp @@ -60,6 +60,7 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) // ok, this can basically be anything. // no restrictions. + fir::Value* returnValue = 0; auto elmty = this->type->getArrayElementType(); if(this->values.empty()) @@ -73,106 +74,110 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // ok. elmty = infer->getArrayElementType(); - - return CGResult(fir::ConstantDynamicArray::get(fir::DynamicArrayType::get(elmty), - fir::ConstantValue::getZeroValue(elmty->getPointerTo()), z, z)); + returnValue = fir::ConstantDynamicArray::get(fir::DynamicArrayType::get(elmty), + fir::ConstantValue::getZeroValue(elmty->getPointerTo()), z, z); } else if(infer->isArraySliceType()) { elmty = infer->getArrayElementType(); //* note: it's clearly a null pointer, so it must be immutable. - return CGResult(fir::ConstantArraySlice::get(fir::ArraySliceType::get(elmty, false), - fir::ConstantValue::getZeroValue(elmty->getPointerTo()), z)); + returnValue = fir::ConstantArraySlice::get(fir::ArraySliceType::get(elmty, false), + fir::ConstantValue::getZeroValue(elmty->getPointerTo()), z); } else { error(this, "incorrectly inferred type '%s' for empty array literal", infer); } } + else + { + // make a function specifically to initialise this thing + static size_t _id = 0; - // make a function specifically to initialise this thing - - static size_t _id = 0; - - - auto _aty = fir::ArrayType::get(elmty, this->values.size()); - auto array = cs->module->createGlobalVariable(Identifier("_FV_DAR_" + std::to_string(_id++), IdKind::Name), - _aty, fir::ConstantArray::getZeroValue(_aty), false, fir::LinkageType::Internal); + auto _aty = fir::ArrayType::get(elmty, this->values.size()); + auto array = cs->module->createGlobalVariable(Identifier("_FV_DAR_" + std::to_string(_id++), IdKind::Name), + _aty, fir::ConstantArray::getZeroValue(_aty), false, fir::LinkageType::Internal); - { - auto restore = cs->irb.getCurrentBlock(); + { + auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(util::obfuscateIdentifier("init_array", _id - 1), - fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); + fir::Function* func = cs->module->getOrCreateFunction(util::obfuscateIdentifier("init_array", _id - 1), + fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); - fir::IRBlock* entry = cs->irb.addNewBlockInFunction("entry", func); - cs->irb.setCurrentBlock(entry); + fir::IRBlock* entry = cs->irb.addNewBlockInFunction("entry", func); + cs->irb.setCurrentBlock(entry); - auto arrptr = cs->irb.AddressOf(array, true); + auto arrptr = cs->irb.AddressOf(array, true); - std::vector vals; - for(auto v : this->values) - { - auto vl = v->codegen(cs, elmty).value; - if(vl->getType() != elmty) - vl = cs->oneWayAutocast(vl, elmty); + std::vector vals; + for(auto v : this->values) + { + auto vl = v->codegen(cs, elmty).value; + if(vl->getType() != elmty) + vl = cs->oneWayAutocast(vl, elmty); + + if(vl->getType() != elmty) + { + error(v, "mismatched type for array literal; expected element type '%s', found '%s'", + elmty, vl->getType()); + } + + // ok, it works + vals.push_back(vl); + } - if(vl->getType() != elmty) + // ok -- basically unroll the loop, except there's no loop -- so we're just... + // doing a thing. + for(size_t i = 0; i < vals.size(); i++) { - error(v, "mismatched type for array literal; expected element type '%s', found '%s'", - elmty, vl->getType()); + // offset by 1 + fir::Value* ptr = cs->irb.ConstGEP2(arrptr, 0, i); + cs->irb.WritePtr(vals[i], ptr); } - // ok, it works - vals.push_back(vl); + cs->irb.ReturnVoid(); + cs->irb.setCurrentBlock(restore); + + // ok, call the function + cs->irb.Call(func); } - // ok -- basically unroll the loop, except there's no loop -- so we're just... - // doing a thing. - for(size_t i = 0; i < vals.size(); i++) + // return it + if(this->type->isDynamicArrayType()) { - // offset by 1 - fir::Value* ptr = cs->irb.ConstGEP2(arrptr, 0, i); - cs->irb.WritePtr(vals[i], ptr); - } + auto arrptr = cs->irb.AddressOf(array, true); - cs->irb.ReturnVoid(); - cs->irb.setCurrentBlock(restore); + auto aa = cs->irb.CreateValue(this->type->toDynamicArrayType()); - // ok, call the function - cs->irb.Call(func); - } + aa = cs->irb.SetSAAData(aa, cs->irb.ConstGEP2(arrptr, 0, 0)); + aa = cs->irb.SetSAALength(aa, fir::ConstantInt::getNative(this->values.size())); + aa = cs->irb.SetSAACapacity(aa, fir::ConstantInt::getNative(-1)); + aa = cs->irb.SetSAARefCountPointer(aa, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())); - // return it - if(this->type->isDynamicArrayType()) - { - auto arrptr = cs->irb.AddressOf(array, true); + returnValue = aa; + } + else if(this->type->isArraySliceType()) + { + auto arrptr = cs->irb.AddressOf(array, true); - auto aa = cs->irb.CreateValue(this->type->toDynamicArrayType()); + auto aa = cs->irb.CreateValue(this->type->toArraySliceType()); - aa = cs->irb.SetSAAData(aa, cs->irb.ConstGEP2(arrptr, 0, 0)); - aa = cs->irb.SetSAALength(aa, fir::ConstantInt::getNative(this->values.size())); - aa = cs->irb.SetSAACapacity(aa, fir::ConstantInt::getNative(-1)); - aa = cs->irb.SetSAARefCountPointer(aa, fir::ConstantValue::getZeroValue(fir::Type::getNativeWordPtr())); + aa = cs->irb.SetArraySliceData(aa, cs->irb.PointerTypeCast(cs->irb.ConstGEP2(arrptr, 0, 0), elmty->getPointerTo())); + aa = cs->irb.SetArraySliceLength(aa, fir::ConstantInt::getNative(this->values.size())); - return CGResult(aa); + returnValue = aa; + } + else + { + error(this, "what???"); + } } - else if(this->type->isArraySliceType()) - { - auto arrptr = cs->irb.AddressOf(array, true); - - auto aa = cs->irb.CreateValue(this->type->toArraySliceType()); - aa = cs->irb.SetArraySliceData(aa, cs->irb.PointerTypeCast(cs->irb.ConstGEP2(arrptr, 0, 0), elmty->getPointerTo())); - aa = cs->irb.SetArraySliceLength(aa, fir::ConstantInt::getNative(this->values.size())); + iceAssert(returnValue); + cs->addRAIIOrRCValueIfNecessary(returnValue); - return CGResult(aa); - } - else - { - error(this, "what???"); - } + return CGResult(returnValue); } else { diff --git a/source/codegen/subscript.cpp b/source/codegen/subscript.cpp index 9d987296..b1099e3d 100644 --- a/source/codegen/subscript.cpp +++ b/source/codegen/subscript.cpp @@ -34,8 +34,7 @@ CGResult sst::SubscriptOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) // TODO: LVALUE HOLE if(lr->islvalue()) { - datapointer = cs->irb.GEP2(cs->irb.AddressOf(lr.value, true), fir::ConstantInt::getNative(0), - fir::ConstantInt::getNative(0)); + datapointer = cs->irb.ConstGEP2(cs->irb.AddressOf(lr.value, true), 0, 0); maxlength = fir::ConstantInt::getNative(lt->toArrayType()->getArraySize()); } else diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 339b84b4..14d8b229 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -919,6 +919,11 @@ namespace fir return this->Call(fn, { p1, p2, p3 }, vname); } + Value* IRBuilder::Call(Function* fn, Value* p1, Value* p2, Value* p3, Value* p4, const std::string& vname) + { + return this->Call(fn, { p1, p2, p3, p4 }, vname); + } + Value* IRBuilder::Call(Function* fn, const std::vector& args, const std::string& vname) { diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index f4988670..a9801476 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -275,16 +275,13 @@ namespace interp } else if(auto cs = dcast(fir::ConstantCharSlice, c)) { - auto str = cs->getValue(); + auto str = makeGlobalString(is, cs->getValue()); + auto ptr = fir::ConstantBitcast::get(fir::ConstantInt::getUNative(reinterpret_cast(str)), fir::Type::getInt8Ptr()); + auto len = fir::ConstantInt::getNative(cs->getValue().size()); - interp::Value ret; - ret.dataSize = sizeof(char*); - ret.type = cs->getType(); - ret.val = cs; - - auto s = makeGlobalString(is, str); + auto bytecount = getSizeOfType(ptr->getType()) + getSizeOfType(len->getType()); - setValueRaw(is, &ret, &s, sizeof(char*)); + auto ret = constructStructThingy(cs, bytecount, { ptr, len }); return (cachedConstants[c] = ret); } diff --git a/source/include/ir/irbuilder.h b/source/include/ir/irbuilder.h index cbb7694d..edf0624a 100644 --- a/source/include/ir/irbuilder.h +++ b/source/include/ir/irbuilder.h @@ -77,6 +77,7 @@ namespace fir Value* Call(Function* fn, Value* p1, const std::string& vname = ""); Value* Call(Function* fn, Value* p1, Value* p2, const std::string& vname = ""); Value* Call(Function* fn, Value* p1, Value* p2, Value* p3, const std::string& vname = ""); + Value* Call(Function* fn, Value* p1, Value* p2, Value* p3, Value* p4, const std::string& vname = ""); Value* Call(Function* fn, const std::vector& args, const std::string& vname = ""); Value* Call(Function* fn, const std::initializer_list& args, const std::string& vname = ""); diff --git a/source/typecheck/arithmetic.cpp b/source/typecheck/arithmetic.cpp index 69e05a9d..35dfd44f 100644 --- a/source/typecheck/arithmetic.cpp +++ b/source/typecheck/arithmetic.cpp @@ -113,12 +113,15 @@ fir::Type* sst::TypecheckState::getBinaryOpResultType(fir::Type* left, fir::Type else if(left->isPrimitiveType() && right->isPrimitiveType() && left == right) return left; - else if(left->isStringType() && right->isStringType()) + else if(left->isStringType() && (right->isStringType() || right->isCharSliceType() || right->isCharType())) return fir::Type::getString(); else if(left->isDynamicArrayType() && right->isDynamicArrayType() && left == right) return left; + else if(left->isDynamicArrayType() && left->getArrayElementType() == right) + return left; + else if((left->isConstantNumberType() && right->isPrimitiveType()) || (left->isPrimitiveType() && right->isConstantNumberType())) return (left->isConstantNumberType() ? right : left); diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 0d499f24..1bfe8931 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -372,6 +372,16 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d fir::ArraySliceType::get(type->getArrayElementType(), false)); } } + else if(fc->name == names::array::FN_POP) + { + if(!type->isDynamicArrayType()) + error(fc, "'pop' method can only be called on dynamic arrays"); + + res = type->getArrayElementType(); + + if(fc->args.size() != 0) + error(fc, "builtin array method 'pop' expects no arguments, found %d instead", fc->args.size()); + } // fallthrough @@ -396,7 +406,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d // TODO: Extension support here fir::Type* res = 0; if(vr->name == names::enumeration::FIELD_NAME) - res = fir::Type::getString(); + res = fir::Type::getCharSlice(false); else if(vr->name == names::enumeration::FIELD_INDEX) res = fir::Type::getNativeWord(); From 6b4541137410be63a42d47bf3c1c047f65135429 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 21:51:29 +0800 Subject: [PATCH 042/129] add more tests to increase language coverage --- build/tester.flx | 6 ++ build/tests/basic.flx | 26 ++++++++ build/ultratiny.flx | 152 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 build/tests/basic.flx diff --git a/build/tester.flx b/build/tester.flx index 4dfcc152..66045496 100644 --- a/build/tester.flx +++ b/build/tester.flx @@ -24,6 +24,7 @@ import "tests/arraytest.flx" import "tests/functions.flx" import "tests/unions.flx" import "tests/using.flx" +import "tests/basic.flx" fn runTests() { @@ -46,6 +47,7 @@ fn runTests() let unionsTitle = " *** UNIONS TEST *** \n" let usingTitle = " *** USING TEST *** \n" let miscTitle = " *** MISCELLANEOUS TESTS *** \n" + let basicTitle = " *** BASIC TESTS *** \n" let thinLine = "----------------------------------------\n" let endTitle = "============ TESTS COMPLETE ============\n" @@ -167,6 +169,10 @@ fn runTests() // miscellaneousTests() std::io::print("\n\n\n") + std::io::print("%%", basicTitle, thinLine) + test_basic::doBasicTest() + std::io::print("\n\n\n") + // fin. std::io::print("%\n\n\n\n\n", endTitle) } diff --git a/build/tests/basic.flx b/build/tests/basic.flx new file mode 100644 index 00000000..f149ec73 --- /dev/null +++ b/build/tests/basic.flx @@ -0,0 +1,26 @@ +// basic.flx +// Copyright (c) 2019, zhiayang, Apache License 2.0. + +export test_basic +import libc as _ + +public fn doBasicTest() +{ + // test logical operators + do { + fn t() -> bool { printf("T "); return true } + fn f() -> bool { printf("F "); return false } + + if t() || f() => printf("yes\n") + if t() && f() => printf("no\n") + } + + // test assignment & appending + do { + var xs: [str] = [ "foo", "bar" ] + xs += "qux" + + for x in xs => printf("%d ", x) + printf("\n") + } +} diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 909c3d87..6c092105 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -4,7 +4,9 @@ export ultratiny -// import libc as _ +import libc as _ +import std::io as _ + // import std::math /* @compiler_support["raii_trait::drop"] trait Drop { fn deinit() } @@ -51,8 +53,6 @@ fn stuff2() -> Bar } */ -ffi fn printf(fmt: &i8, ...) -> i32 - struct Thicc { x: i64 @@ -70,12 +70,154 @@ struct Thicc // let q = stuff() // let p = stuff2() // printf("q = %d\n", q) - let x = Flat(x: 3, y: 20) + // let x = Flat(x: 3, y: 20) + + // printf("%d, %d, %d\n", sizeof(x), sizeof(Thicc), sizeof(Flat)) - printf("%d, %d, %d\n", sizeof(x), sizeof(Thicc), sizeof(Flat)) + doBasicTest() } +public fn doBasicTest() +{ + + // fn print_array(xs: [T:]) + // { + // for x in xs => print("% ", x) + // println() + // } + + + // // test logical operators + // do { + // fn t() -> bool { print("T "); return true } + // fn f() -> bool { print("F "); return false } + + // if t() || f() => println("yes") + // if t() && f() => println("no") + + // printf("\n\n") + // } + + // // test tuple assignment + // do { + // var (a, b) = (10, 20) + // println("a = %, b = %", a, b) + + // (b, a) = (a, b) + // println("swapped: a = %, b = %\n", a, b) + // } + + // // test assignment & appending + // do { + // do { + // var xs: [str] = [ "foo", "bar" ] + // xs += "qux" + + // var ys: [i64] = [ 1, 2, 3, 4 ] + // ys += 5 as i64 + + // print_array(xs) + // print_array(ys) + // } + + // println() + + // do { + // var xs: [str] = [ "foo", "bar" ] + // xs += [ "pepega", "kekw" ] + + // var ys: [i64] = [ 1, 2, 3, 4 ] + // ys += [ 5, 6, 7, 8 ] + + // print_array(xs) + // print_array(ys) + // } + + // println() + + // do { + // var s = string("some string") + // s += "_appendage" + + // var t = string("an underscore: ") + // t += '_' + + // println("s = %", s) + // println("t = %", t) + // } + // } + + // println() + + // // test + on non-arithmetics + // do { + // let a = string("hello ") + "world" + // println("a = %", a) + + // let b = string("an asterisk: ") + '*' + // println("b = %\n", b) + + // let c: [int] = [ 2, 3, 5, 7 ] + // let d: [int] = [ 11, 13, 17, 19 ] + + // print_array(c + d) + // } + + // println() + + // test builtin functions on aggregate data types + do { + // var a: [int] = [ 2, 3, 5, 7 ] + // print("% : ", a.pop()) + // print_array(a) + + // var b: [int: 3] = [ 1, 4, 9 ] + // // drop to printf for %p support + // printf("b.ptr = %p, b.length = %d\n\n", b.ptr, b.length) + + // var s = string("thank you ございました") + // println("\"%\".count = % (expect 16)\n", s, s.count) + + // var r = 0...10 step 2 + // for i in r => print("% ", i) + // println("\nstart: %, end: %, step: %", r.begin, r.end, r.step) + + // var c: any = 30 + // println("c.typeid = %, c.refcount = %", c.id, c.refcount) + + enum E + { + case ONE + case TWO + } + + var e = E::ONE + println("e.index = %, e.value = %, e.name = %", e.index, e.value, e.name) + // let s = e.name + // println("s = %", s) + + + // fn test(x: any) + // { + // var out = string("foo: ") + // if x is str => out += x as str + + // printf("out = %s\n", out) + // } + + // // let s = "hello" + // // // printf("s = %s\n", s as str) + // test(s) + + } + + + println() +} + + + /* raii traits checklist: From ecf8d90b9c22f5e3d67a1a7ae432a2850527473b Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 22:13:29 +0800 Subject: [PATCH 043/129] move the basic tests to the real place --- build/tests/basic.flx | 147 +++++++++++++++++++++++++++++++++++++--- build/ultratiny.flx | 141 -------------------------------------- source/codegen/call.cpp | 13 ++-- 3 files changed, 148 insertions(+), 153 deletions(-) diff --git a/build/tests/basic.flx b/build/tests/basic.flx index f149ec73..ed3af988 100644 --- a/build/tests/basic.flx +++ b/build/tests/basic.flx @@ -3,24 +3,155 @@ export test_basic import libc as _ +import std::io as _ +// TODO: reorganise these tests if possible +// eg. move them to a more appropriate file? I think some of these might fit. public fn doBasicTest() { + // put a default argument here so we can test that. + fn print_array(xs: [T:]) + { + for x in xs => print("% ", x) + println() + } + + // test logical operators do { - fn t() -> bool { printf("T "); return true } - fn f() -> bool { printf("F "); return false } + fn t() -> bool { print("T "); return true } + fn f() -> bool { print("F "); return false } + + if t() || f() => println("yes") + if t() && f() => println("no") + + printf("\n\n") + } + + // test tuple assignment + do { + var (a, b) = (10, 20) + println("a = %, b = %", a, b) - if t() || f() => printf("yes\n") - if t() && f() => printf("no\n") + (b, a) = (a, b) + println("swapped: a = %, b = %\n", a, b) } // test assignment & appending do { - var xs: [str] = [ "foo", "bar" ] - xs += "qux" + do { + var xs: [str] = [ "foo", "bar" ] + xs += "qux" + + var ys: [i64] = [ 1, 2, 3, 4 ] + ys += 5 as i64 + + print_array(xs) + print_array(ys) + } + + println() + + do { + var xs: [str] = [ "foo", "bar" ] + xs += [ "pepega", "kekw" ] + + var ys: [i64] = [ 1, 2, 3, 4 ] + ys += [ 5, 6, 7, 8 ] + + print_array(xs) + print_array(ys) + } + + println() + + do { + var s = string("some string") + s += "_appendage" + + var t = string("an underscore: ") + t += '_' + + println("s = %", s) + println("t = %", t) + } + } + + println() + + // test + on non-arithmetics + do { + let a = string("hello ") + "world" + println("a = %", a) + + let b = string("an asterisk: ") + '*' + println("b = %\n", b) + + let c: [int] = [ 2, 3, 5, 7 ] + let d: [int] = [ 11, 13, 17, 19 ] + + print_array(c + d) + } + + println() + + // test builtin functions on aggregate data types + do { + var a: [int] = [ 2, 3, 5, 7 ] + print("% : ", a.pop()) + print_array(a) - for x in xs => printf("%d ", x) - printf("\n") + var b: [int: 3] = [ 1, 4, 9 ] + // drop to printf for %p support + printf("b.ptr = %p, b.length = %d\n\n", b.ptr, b.length) + + var s = string("thank you ございました") + println("\"%\".count = % (expect 16)\n", s, s.count) + + var r = 0...10 step 2 + for i in r => print("% ", i) + println("\nstart: %, end: %, step: %", r.begin, r.end, r.step) + + var c: any = 30 + println("c.typeid = %, c.refcount = %", c.id, c.refcount) + + enum E + { + case ONE + case TWO + } + + var e = E::TWO + println("e.index = %, e.value = %, e.name = %", e.index, e.value, e.name) } + + + println() + + // other strange things + do { + struct Foozle + { + x: fn(int) -> int + + fn foo() -> int + { + return x(30) + } + } + + fn triple(x: int) -> int => x * 3 + + let f = Foozle(x: triple) + println("f = %", f.foo()) + + let g = triple + println("g(76) = %", g(76)) + } + + + println() } + +// well just a test. +#run println("** hello, world!") diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 6c092105..25c22b0b 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -78,147 +78,6 @@ struct Thicc } -public fn doBasicTest() -{ - - // fn print_array(xs: [T:]) - // { - // for x in xs => print("% ", x) - // println() - // } - - - // // test logical operators - // do { - // fn t() -> bool { print("T "); return true } - // fn f() -> bool { print("F "); return false } - - // if t() || f() => println("yes") - // if t() && f() => println("no") - - // printf("\n\n") - // } - - // // test tuple assignment - // do { - // var (a, b) = (10, 20) - // println("a = %, b = %", a, b) - - // (b, a) = (a, b) - // println("swapped: a = %, b = %\n", a, b) - // } - - // // test assignment & appending - // do { - // do { - // var xs: [str] = [ "foo", "bar" ] - // xs += "qux" - - // var ys: [i64] = [ 1, 2, 3, 4 ] - // ys += 5 as i64 - - // print_array(xs) - // print_array(ys) - // } - - // println() - - // do { - // var xs: [str] = [ "foo", "bar" ] - // xs += [ "pepega", "kekw" ] - - // var ys: [i64] = [ 1, 2, 3, 4 ] - // ys += [ 5, 6, 7, 8 ] - - // print_array(xs) - // print_array(ys) - // } - - // println() - - // do { - // var s = string("some string") - // s += "_appendage" - - // var t = string("an underscore: ") - // t += '_' - - // println("s = %", s) - // println("t = %", t) - // } - // } - - // println() - - // // test + on non-arithmetics - // do { - // let a = string("hello ") + "world" - // println("a = %", a) - - // let b = string("an asterisk: ") + '*' - // println("b = %\n", b) - - // let c: [int] = [ 2, 3, 5, 7 ] - // let d: [int] = [ 11, 13, 17, 19 ] - - // print_array(c + d) - // } - - // println() - - // test builtin functions on aggregate data types - do { - // var a: [int] = [ 2, 3, 5, 7 ] - // print("% : ", a.pop()) - // print_array(a) - - // var b: [int: 3] = [ 1, 4, 9 ] - // // drop to printf for %p support - // printf("b.ptr = %p, b.length = %d\n\n", b.ptr, b.length) - - // var s = string("thank you ございました") - // println("\"%\".count = % (expect 16)\n", s, s.count) - - // var r = 0...10 step 2 - // for i in r => print("% ", i) - // println("\nstart: %, end: %, step: %", r.begin, r.end, r.step) - - // var c: any = 30 - // println("c.typeid = %, c.refcount = %", c.id, c.refcount) - - enum E - { - case ONE - case TWO - } - - var e = E::ONE - println("e.index = %, e.value = %, e.name = %", e.index, e.value, e.name) - // let s = e.name - // println("s = %", s) - - - // fn test(x: any) - // { - // var out = string("foo: ") - // if x is str => out += x as str - - // printf("out = %s\n", out) - // } - - // // let s = "hello" - // // // printf("s = %s\n", s as str) - // test(s) - - } - - - println() -} - - - - /* raii traits checklist: diff --git a/source/codegen/call.cpp b/source/codegen/call.cpp index d80a0c30..ecede101 100644 --- a/source/codegen/call.cpp +++ b/source/codegen/call.cpp @@ -294,12 +294,17 @@ CGResult sst::FunctionCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) } else { - auto vt = vf->getType(); - iceAssert(vt->isPointerType() && vt->getPointerElementType()->isFunctionType()); + // we should have disallowed this already in the typechecker. + // TODO: is the usecase then to just cast to a function type? + // eg. let entry = (0x400000) as (fn()->int) or something + iceAssert(false && "somehow you got a pointer-to-function?!"); - ft = vt->getPointerElementType()->toFunctionType(); + // auto vt = vf->getType(); + // iceAssert(vt->isPointerType() && vt->getPointerElementType()->isFunctionType()); - warn(this, "prefer using functions to function pointers"); + // ft = vt->getPointerElementType()->toFunctionType(); + + // warn(this, "prefer using functions to function pointers"); } iceAssert(ft); From 0ede90e67283413cb4c644170cdb5a3d364a0d8b Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 22:17:39 +0800 Subject: [PATCH 044/129] change @compiler_support to be a normal UA instead of a special token --- build/ultratiny.flx | 24 ++++-------------------- source/frontend/lexer.cpp | 6 ------ source/frontend/parser/expr.cpp | 2 +- source/include/lexer.h | 1 - source/include/string_consts.h | 2 +- 5 files changed, 6 insertions(+), 29 deletions(-) diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 25c22b0b..c70d8258 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -8,7 +8,7 @@ import libc as _ import std::io as _ // import std::math -/* + @compiler_support["raii_trait::drop"] trait Drop { fn deinit() } @compiler_support["raii_trait::copy"] trait Copy { fn copy(other: &self) } @compiler_support["raii_trait::move"] trait Move { fn move(other: &mut self) } @@ -51,30 +51,14 @@ fn stuff2() -> Bar { return Bar(x: 57) } - */ -struct Thicc -{ - x: i64 - y: i8 -} -@packed struct Flat -{ - x: i64 - y: i8 -} @entry fn main() { - // let q = stuff() - // let p = stuff2() - // printf("q = %d\n", q) - // let x = Flat(x: 3, y: 20) - - // printf("%d, %d, %d\n", sizeof(x), sizeof(Thicc), sizeof(Flat)) - - doBasicTest() + let q = stuff() + let p = stuff2() + printf("q = %d\n", q) } diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index c5eebfe6..45dcaa2a 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -425,12 +425,6 @@ namespace lexer tok.text = "@platform"; read = 9; } - else if(hasPrefix(stream, "@compiler_support")) - { - tok.type = TokenType::Attr_CompilerSupport; - tok.text = "@compiler_support"; - read = 17; - } // directives else if(hasPrefix(stream, "#if")) diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 9e761845..b5b8bb97 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -121,7 +121,7 @@ namespace parser // note: due to poor API design on my part, if there is no attribute with that name then ::get() // returns an empty UA, which has a blank name -- so we check that instead. - if(auto ua = attrs.get("@compiler_support"); !ua.name.empty() && ua.args.size() != 1) + if(auto ua = attrs.get("compiler_support"); !ua.name.empty() && ua.args.size() != 1) error(ret, "@compiler_support requires exactly one argument"); // actually that's it diff --git a/source/include/lexer.h b/source/include/lexer.h index 4b0fb0d9..6e580498 100644 --- a/source/include/lexer.h +++ b/source/include/lexer.h @@ -126,7 +126,6 @@ namespace lexer Attr_NoMangle, Attr_Operator, Attr_Platform, - Attr_CompilerSupport, Attr_ATTRS_END, diff --git a/source/include/string_consts.h b/source/include/string_consts.h index b65dba9f..a96826bb 100644 --- a/source/include/string_consts.h +++ b/source/include/string_consts.h @@ -9,7 +9,7 @@ namespace strs { namespace attrs { - inline constexpr auto COMPILER_SUPPORT = "@compiler_support"; + inline constexpr auto COMPILER_SUPPORT = "compiler_support"; } namespace names From dc1348e1d26b0f8871c2b60188cc60492bd99b57 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 20 Oct 2019 23:11:15 +0800 Subject: [PATCH 045/129] check-virtually-compatible is broken; doesn't affect main tests --- build/ultratiny.flx | 11 +-- source/codegen/raii.cpp | 126 ++++++++++++++++++++++----------- source/fir/Types/ClassType.cpp | 9 +-- 3 files changed, 93 insertions(+), 53 deletions(-) diff --git a/build/ultratiny.flx b/build/ultratiny.flx index c70d8258..51815f74 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -42,22 +42,23 @@ class Bar } } -fn stuff() -> Foo +fn one() -> Foo { return Foo(data: 33) } -fn stuff2() -> Bar +fn two(x: Foo) { - return Bar(x: 57) + printf("x.data = %d\n", x.data) } @entry fn main() { - let q = stuff() - let p = stuff2() + let q = one() + two(q) + printf("q = %d\n", q) } diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index d3b339b4..56cf7945 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -74,6 +74,32 @@ namespace cgn } + static fir::Function* getImplementationForRAIITrait(CodegenState* cs, const std::string& name, fir::Type* ty) + { + if(auto it = cs->compilerSupportDefinitions.find(name); it != cs->compilerSupportDefinitions.end()) + { + auto trt = dcast(sst::TraitDefn, it->second); + if(!trt) error("invalid use of @compiler_support[\"%s\"] on non-trait definition!", name); + + iceAssert(trt->methods.size() == 1); + + auto str = dcast(sst::StructDefn, cs->typeDefnMap[ty]); + iceAssert(str); + + auto target = cs->findMatchingMethodInType(str, trt->methods[0]); + if(target) + { + // the inferred type should be the receiver type + auto ret = target->codegen(cs, ty); + return dcast(fir::Function, ret.value); + } + } + + return 0; + } + + + void CodegenState::callDestructor(fir::Value* val) { @@ -94,47 +120,19 @@ namespace cgn // call the auto one. this will handle calling base class destructors for us! this->irb.Call(cls->getInlineDestructor(), selfptr); } - else if(auto it = this->compilerSupportDefinitions.find(strs::names::support::RAII_TRAIT_DROP); - it != this->compilerSupportDefinitions.end()) + else { - auto trt = dcast(sst::TraitDefn, it->second); - if(!trt) error("invalid use of @compiler_support[\"raii_trait::drop\"] on non-trait definition!"); + auto destructor = getImplementationForRAIITrait(this, strs::names::support::RAII_TRAIT_DROP, val->getType()); - iceAssert(trt->methods.size() == 1); - - auto str = dcast(sst::StructDefn, this->typeDefnMap[val->getType()]); - iceAssert(str); - - auto destructor = this->findMatchingMethodInType(str, trt->methods[0]); - if(destructor) - { - this->irb.Call(dcast(fir::Function, destructor->codegen(this).value), selfptr); - return; - } + // well if you didn't implement it then the typechecker would have already complained about you so... + iceAssert(destructor); + this->irb.Call(destructor, selfptr); } } - static fir::ClassType* doChecks(CodegenState* cs, fir::Value* from, fir::Value* target) - { - // this cleans up the callsites so we can just unconditionally call this. - if(!from->getType()->isClassType()) - { - cs->irb.Store(from, target); - return 0; - } - - auto clsty = from->getType()->toClassType(); - - // TODO: this is a shitty error message. - if(!target->islvalue()) - error(cs->loc(), "invalid operation on non-lvalue"); - - return clsty; - } - static void doMemberWiseStuffIfNecessary(CodegenState* cs, fir::ClassType* clsty, fir::Value* from, fir::Value* target, bool move) { // check if there are even any class types inside. if not, do the simple thing! @@ -175,10 +173,28 @@ namespace cgn + static fir::ClassType* doChecks(CodegenState* cs, fir::Value* from, fir::Value* target) + { + // this cleans up the callsites so we can just unconditionally call this. + if(!from->getType()->isClassType()) + { + cs->irb.Store(from, target); + return 0; + } + + auto clsty = from->getType()->toClassType(); + + // TODO: this is a shitty error message. + if(!target->islvalue()) + error(cs->loc(), "invalid operation on non-lvalue"); + + return clsty; + } + fir::Value* CodegenState::copyRAIIValue(fir::Value* value) { - if(!value->getType()->isClassType()) + if(!typeHasCopyConstructor(value->getType())) return value; // this will zero-initialise! @@ -194,8 +210,11 @@ namespace cgn { iceAssert(from->getType() == target->getType()); - auto clsty = doChecks(this, from, target); - if(!clsty) return; + if(!typeHasCopyConstructor(from->getType())) + { + this->irb.Store(from, target); + return; + } if(!from->islvalue() && enableMoving) { @@ -203,20 +222,43 @@ namespace cgn return; } - // if there is a copy-constructor, then we will call the copy constructor. - if(auto copycon = clsty->getCopyConstructor(); copycon) + // there's probably a better way to structure this, but i can't be bothered right now + // or ever. it's just 2 lines of code dupe anyway. so sue me. + + if(from->getType()->isClassType()) { - auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); - auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + auto clsty = from->getType()->toClassType(); - this->irb.Call(copycon, selfptr, otherptr); + // if there is a copy-constructor, then we will call the copy constructor. + if(auto copycon = clsty->getCopyConstructor(); copycon) + { + auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); + auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + + this->irb.Call(copycon, selfptr, otherptr); + } + else + { + doMemberWiseStuffIfNecessary(this, clsty, from, target, /* move: */ false); + } } else { - doMemberWiseStuffIfNecessary(this, clsty, from, target, /* move: */ false); + auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); + auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + + // well we got here, so we know at least the type has a copy constructor somewhere. + auto copycon = getImplementationForRAIITrait(this, strs::names::support::RAII_TRAIT_COPY, from->getType()); + + // again, typechecking would have complained prior to this + iceAssert(copycon); + this->irb.Call(copycon, selfptr, otherptr); } } + + + void CodegenState::moveRAIIValue(fir::Value* from, fir::Value* target) { iceAssert(from->getType() == target->getType()); diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 17fb7c51..f07c7dbc 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -319,14 +319,11 @@ namespace fir return false; } - auto bc = base->getPointerElementType()->toClassType(); - auto dc = derv->getPointerElementType()->toClassType(); + auto bce = base->getPointerElementType(); + auto dce = derv->getPointerElementType(); - if(!bc->hasParent(dc)) - { - debuglogln("%s is not a parent of %s", dc->str(), bc->str()); + if(bce->isClassType() && dce->isClassType() && !bce->toClassType()->hasParent(dce)) return false; - } } return true; From 3c75576bf12007e91989ca799e82ea37de705e34 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 23 Oct 2019 14:49:44 +0800 Subject: [PATCH 046/129] misc fixes --- external/mpreal/mpreal.h | 15 +++-- source/backend/backend.cpp | 3 +- source/backend/interp/driver.cpp | 2 +- source/backend/llvm/linker.cpp | 3 +- source/backend/x64AsmBackend.cpp | 2 +- source/codegen/function.cpp | 4 +- source/codegen/glue/misc.cpp | 2 +- source/fir/ConstantValue.cpp | 20 +++--- source/fir/IRBuilder.cpp | 92 +++++++++++++++----------- source/fir/Module.cpp | 39 ++++++----- source/fir/Types/ClassType.cpp | 18 ++--- source/fir/Types/SingleTypes.cpp | 20 +++--- source/fir/Types/StructType.cpp | 8 +-- source/fir/Types/Type.cpp | 4 +- source/fir/Value.cpp | 2 +- source/fir/interp/interpreter.cpp | 31 ++++++--- source/frontend/arguments.cpp | 8 +-- source/frontend/dependencies.cpp | 4 +- source/frontend/errors.cpp | 4 +- source/frontend/file.cpp | 7 +- source/frontend/parser/controlflow.cpp | 4 +- source/frontend/parser/expr.cpp | 2 +- source/frontend/parser/toplevel.cpp | 2 +- source/include/ast.h | 20 +++--- source/include/backend.h | 7 +- source/include/backends/interp.h | 2 +- source/include/backends/llvm.h | 2 +- source/include/container.h | 8 +-- source/include/defs.h | 17 +++-- source/include/errors.h | 3 - source/include/frontend.h | 6 +- source/include/gluecode.h | 2 +- source/include/ir/constant.h | 16 ++--- source/include/ir/irbuilder.h | 16 ++--- source/include/ir/module.h | 13 ++-- source/include/ir/type.h | 2 +- source/include/ir/value.h | 2 +- source/include/parser.h | 2 +- source/include/pts.h | 5 -- source/include/sst.h | 4 +- source/include/typecheck.h | 1 - source/include/utils.h | 14 ++-- source/include/zpr.h | 10 +-- source/misc/identifier.cpp | 38 ++--------- 44 files changed, 240 insertions(+), 246 deletions(-) diff --git a/external/mpreal/mpreal.h b/external/mpreal/mpreal.h index b3d9b943..101a0b94 100644 --- a/external/mpreal/mpreal.h +++ b/external/mpreal/mpreal.h @@ -142,7 +142,7 @@ #endif // Less important options -#define MPREAL_DOUBLE_BITS_OVERFLOW -1 // Triggers overflow exception during conversion to double if mpreal +#define MPREAL_DOUBLE_BITS_OVERFLOW (-1) // Triggers overflow exception during conversion to double if mpreal // cannot fit in MPREAL_DOUBLE_BITS_OVERFLOW bits // = -1 disables overflow checks (default) @@ -194,8 +194,8 @@ class mpreal { ~mpreal(); #ifdef MPREAL_HAVE_MOVE_SUPPORT - mpreal& operator=(mpreal&& v); - mpreal(mpreal&& u); + mpreal& operator=(mpreal&& v) noexcept; + mpreal(mpreal&& u) noexcept; #endif // Operations @@ -603,7 +603,7 @@ inline mpreal::mpreal(const mpreal& u) } #ifdef MPREAL_HAVE_MOVE_SUPPORT -inline mpreal::mpreal(mpreal&& other) +inline mpreal::mpreal(mpreal&& other) noexcept { mpfr_set_uninitialized(mpfr_ptr()); // make sure "other" holds no pointer to actual data mpfr_swap(mpfr_ptr(), other.mpfr_ptr()); @@ -611,7 +611,7 @@ inline mpreal::mpreal(mpreal&& other) MPREAL_MSVC_DEBUGVIEW_CODE; } -inline mpreal& mpreal::operator=(mpreal&& other) +inline mpreal& mpreal::operator=(mpreal&& other) noexcept { mpfr_swap(mpfr_ptr(), other.mpfr_ptr()); @@ -1093,7 +1093,8 @@ inline mpreal& mpreal::operator=(const std::string& s) template inline mpreal& mpreal::operator= (const std::complex& z) { - return *this = z.real(); + *this = z.real(); + return *this; } ////////////////////////////////////////////////////////////////////////// @@ -3004,7 +3005,7 @@ namespace std { // we are allowed to extend namespace std with specializations only template <> - inline void swap(mpfr::mpreal& x, mpfr::mpreal& y) + inline void swap(mpfr::mpreal& x, mpfr::mpreal& y) noexcept { return mpfr::swap(x, y); } diff --git a/source/backend/backend.cpp b/source/backend/backend.cpp index 8af1c853..a5339548 100644 --- a/source/backend/backend.cpp +++ b/source/backend/backend.cpp @@ -7,7 +7,8 @@ namespace backend { - Backend* Backend::getBackendFromOption(BackendOption opt, CompiledData& cd, std::vector in, std::string out) + Backend* Backend::getBackendFromOption(BackendOption opt, CompiledData& cd, + const std::vector& in, const std::string& out) { switch(opt) { diff --git a/source/backend/interp/driver.cpp b/source/backend/interp/driver.cpp index 3186d32d..e2baa0e5 100644 --- a/source/backend/interp/driver.cpp +++ b/source/backend/interp/driver.cpp @@ -32,7 +32,7 @@ namespace backend using namespace fir; using namespace fir::interp; - FIRInterpBackend::FIRInterpBackend(CompiledData& dat, std::vector inputs, std::string output) + FIRInterpBackend::FIRInterpBackend(CompiledData& dat, const std::vector& inputs, const std::string& output) : Backend(BackendCaps::JIT, dat, inputs, output) { platform::compiler::performSelfDlOpen(); diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index 2183455f..c182bf42 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -90,7 +90,8 @@ namespace backend return globalContext; } - LLVMBackend::LLVMBackend(CompiledData& dat, std::vector inputs, std::string output) : Backend(BackendCaps::EmitAssembly | BackendCaps::EmitObject | BackendCaps::EmitProgram | BackendCaps::JIT, dat, inputs, output) + LLVMBackend::LLVMBackend(CompiledData& dat, const std::vector& inputs, const std::string& output) + : Backend(BackendCaps::EmitAssembly | BackendCaps::EmitObject | BackendCaps::EmitProgram | BackendCaps::JIT, dat, inputs, output) { } diff --git a/source/backend/x64AsmBackend.cpp b/source/backend/x64AsmBackend.cpp index ab70b257..73738d65 100644 --- a/source/backend/x64AsmBackend.cpp +++ b/source/backend/x64AsmBackend.cpp @@ -6,7 +6,7 @@ namespace backend { - x64Backend::x64Backend(CompiledData& dat, std::vector inputs, std::string output) + x64Backend::x64Backend(CompiledData& dat, const std::vector& inputs, const std::string& output) : Backend(0, dat, inputs, output) { } diff --git a/source/codegen/function.cpp b/source/codegen/function.cpp index ee96776f..857356e7 100644 --- a/source/codegen/function.cpp +++ b/source/codegen/function.cpp @@ -18,7 +18,7 @@ CGResult sst::FunctionDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) std::vector ptypes; - for(auto p : this->params) + for(const auto& p : this->params) ptypes.push_back(p.type); auto ft = fir::FunctionType::get(ptypes, this->returnType); @@ -106,7 +106,7 @@ CGResult sst::ForeignFuncDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) fir::FunctionType* ft = 0; std::vector ptypes; - for(auto p : this->params) + for(const auto& p : this->params) ptypes.push_back(p.type); if(this->isVarArg) diff --git a/source/codegen/glue/misc.cpp b/source/codegen/glue/misc.cpp index 972d20d1..f698bdd2 100644 --- a/source/codegen/glue/misc.cpp +++ b/source/codegen/glue/misc.cpp @@ -11,7 +11,7 @@ namespace cgn { namespace glue { -void printRuntimeError(cgn::CodegenState* cs, fir::Value* pos, std::string message, std::vector args) +void printRuntimeError(cgn::CodegenState* cs, fir::Value* pos, const std::string& message, const std::vector& args) { //! on windows, apparently fprintf doesn't like to work. //! so we just use normal printf. diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 13305ce1..223fa890 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -215,12 +215,12 @@ namespace fir - ConstantStruct* ConstantStruct::get(StructType* st, std::vector members) + ConstantStruct* ConstantStruct::get(StructType* st, const std::vector& members) { return new ConstantStruct(st, members); } - ConstantStruct::ConstantStruct(StructType* st, std::vector members) : ConstantValue(st) + ConstantStruct::ConstantStruct(StructType* st, const std::vector& members) : ConstantValue(st) { if(st->getElementCount() != members.size()) error("mismatched structs: expected %d fields, got %d", st->getElementCount(), members.size()); @@ -243,12 +243,12 @@ namespace fir - ConstantCharSlice* ConstantCharSlice::get(std::string s) + ConstantCharSlice* ConstantCharSlice::get(const std::string& s) { return new ConstantCharSlice(s); } - ConstantCharSlice::ConstantCharSlice(std::string s) : ConstantValue(fir::Type::getCharSlice(false)) + ConstantCharSlice::ConstantCharSlice(const std::string& s) : ConstantValue(fir::Type::getCharSlice(false)) { this->str = s; } @@ -260,7 +260,7 @@ namespace fir - ConstantTuple* ConstantTuple::get(std::vector mems) + ConstantTuple* ConstantTuple::get(const std::vector& mems) { return new ConstantTuple(mems); } @@ -270,9 +270,11 @@ namespace fir return this->values; } - static std::vector mapTypes(std::vector vs) + static std::vector mapTypes(const std::vector& vs) { std::vector ret; + ret.reserve(vs.size()); + for(auto v : vs) ret.push_back(v->getType()); @@ -280,7 +282,7 @@ namespace fir } // well this is stupid. - ConstantTuple::ConstantTuple(std::vector mems) : fir::ConstantValue(fir::TupleType::get(mapTypes(mems))) + ConstantTuple::ConstantTuple(const std::vector& mems) : fir::ConstantValue(fir::TupleType::get(mapTypes(mems))) { this->values = mems; } @@ -338,12 +340,12 @@ namespace fir - ConstantArray* ConstantArray::get(Type* type, std::vector vals) + ConstantArray* ConstantArray::get(Type* type, const std::vector& vals) { return new ConstantArray(type, vals); } - ConstantArray::ConstantArray(Type* type, std::vector vals) : fir::ConstantValue(type) + ConstantArray::ConstantArray(Type* type, const std::vector& vals) : fir::ConstantValue(type) { this->values = vals; } diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 14d8b229..e076a3f1 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -91,7 +91,7 @@ namespace fir return v; } - static Instruction* getBinaryOpInstruction(IRBlock* parent, std::string ao, Value* vlhs, Value* vrhs) + static Instruction* getBinaryOpInstruction(const std::string& ao, Value* vlhs, Value* vrhs) { OpKind op = OpKind::Invalid; @@ -299,9 +299,9 @@ namespace fir return make_instr(op, false, out, { vlhs, vrhs }); } - Value* IRBuilder::BinaryOp(std::string ao, Value* a, Value* b, const std::string& vname) + Value* IRBuilder::BinaryOp(const std::string& ao, Value* a, Value* b, const std::string& vname) { - Instruction* instr = getBinaryOpInstruction(this->currentBlock, ao, a, b); + Instruction* instr = getBinaryOpInstruction(ao, a, b); if(instr == 0) return 0; return this->addInstruction(instr, vname); @@ -865,25 +865,25 @@ namespace fir auto l = v->getType(); if(l->isIntegerType() && r->isIntegerType()) - return this->IntSizeCast(v, r); + return this->IntSizeCast(v, r, vname); else if(l->isFloatingPointType() && r->isFloatingPointType()) - return (l->getBitWidth() > r->getBitWidth() ? this->FTruncate(v, r) : this->FExtend(v, r)); + return (l->getBitWidth() > r->getBitWidth() ? this->FTruncate(v, r, vname) : this->FExtend(v, r, vname)); else if(l->isIntegerType() && r->isFloatingPointType()) - return this->IntToFloatCast(v, r); + return this->IntToFloatCast(v, r, vname); else if(l->isFloatingPointType() && r->isIntegerType()) - return this->FloatToIntCast(v, r); + return this->FloatToIntCast(v, r, vname); else if(l->isIntegerType() && r->isPointerType()) - return this->IntToPointerCast(v, r); + return this->IntToPointerCast(v, r, vname); else if(l->isPointerType() && r->isIntegerType()) - return this->PointerToIntCast(v, r); + return this->PointerToIntCast(v, r, vname); else if(l->isPointerType() && r->isPointerType()) - return this->PointerTypeCast(v, r); + return this->PointerTypeCast(v, r, vname); // nope. return 0; @@ -935,9 +935,28 @@ namespace fir auto autocastStuff = [this](Value* arg, Type* target) -> Value* { + auto isSlice = [](Type* ty) -> bool { + return ty->isArraySliceType(); + }; + + auto isVariadicSlice = [&isSlice](Type* ty) -> bool { + return isSlice(ty) && ty->toArraySliceType()->isVariadicType(); + }; + + auto getSliceElm = [](Type* ty) -> Type* { + return ty->getArrayElementType(); + }; + + auto isSliceMut = [&isSlice](Type* ty) -> bool { + return isSlice(ty) && ty->toArraySliceType()->isMutable(); + }; + auto at = arg->getType(); - if(at->isArraySliceType() && target->isArraySliceType() && - target->toArraySliceType()->isVariadicType() != at->toArraySliceType()->isVariadicType()) + if((isSlice(at) && isSlice(target) && (isVariadicSlice(at) != isVariadicSlice(target))) + || (isSlice(at) && isSlice(target) + && getSliceElm(at) == getSliceElm(target) + && isSliceMut(at) && !isSliceMut(target) + )) { // silently cast, because they're the same thing // the distinction is solely for the type system's benefit @@ -949,12 +968,6 @@ namespace fir // this is ok. at the llvm level the cast should reduce to a no-op. return this->PointerTypeCast(arg, target); } - else if(at->isArraySliceType() && target->isArraySliceType() - && at->getArrayElementType() == target->getArrayElementType() - && at->toArraySliceType()->isMutable() && !target->toArraySliceType()->isMutable()) - { - return this->Bitcast(arg, target); - } else { return arg; @@ -1213,6 +1226,7 @@ namespace fir : slcelmty->getPointerTo())); slc = this->SetArraySliceLength(slc, this->GetSAALength(saa)); + slc->setName(vname); return slc; } @@ -1245,6 +1259,7 @@ namespace fir auto ret = this->addInstruction(instr, ""); ret->setKind(lval->kind); + ret->setName(vname); return ret; } @@ -1268,7 +1283,7 @@ namespace fir template - static Instruction* doGEPOnCompoundType(T* type, Value* structPtr, size_t memberIndex, Type* memberTy) + static Instruction* doGEPOnCompoundType(T* type, Value* structPtr, size_t memberIndex) { if(!structPtr->islvalue()) error("irbuilder: cannot do GEP on non-lvalue"); @@ -1292,12 +1307,12 @@ namespace fir if(structPtr->getType()->isStructType()) { auto st = structPtr->getType()->toStructType(); - return this->addInstruction(doGEPOnCompoundType(st, structPtr, memberIndex, st->getElementN(memberIndex)), vname); + return this->addInstruction(doGEPOnCompoundType(st, structPtr, memberIndex), vname); } else if(structPtr->getType()->isTupleType()) { auto tt = structPtr->getType()->toTupleType(); - return this->addInstruction(doGEPOnCompoundType(tt, structPtr, memberIndex, tt->getElementN(memberIndex)), vname); + return this->addInstruction(doGEPOnCompoundType(tt, structPtr, memberIndex), vname); } else if(structPtr->getType()->isClassType()) { @@ -1359,7 +1374,7 @@ namespace fir - void IRBuilder::SetVtable(Value* ptr, Value* table, const std::string& vname) + void IRBuilder::SetVtable(Value* ptr, Value* table) { if(!ptr->islvalue()) error("irbuilder: cannot do set vtable on non-lvalue"); @@ -1370,7 +1385,7 @@ namespace fir Instruction* instr = make_instr(OpKind::Value_GetStructMember, false, Type::getInt8Ptr(), { ptr, ConstantInt::getUNative(0) }, Value::Kind::lvalue); - auto gep = this->addInstruction(instr, vname); + auto gep = this->addInstruction(instr, "__vtable"); this->Store(table, gep); } @@ -1386,7 +1401,7 @@ namespace fir auto ptri = ConstantInt::getUNative(ptrIndex); auto elmi = ConstantInt::getUNative(elmIndex); - return this->GEP2(ptr, ptri, elmi); + return this->GEP2(ptr, ptri, elmi, vname); } // equivalent to GEP(ptr*, ptrIndex, elmIndex) @@ -1455,7 +1470,10 @@ namespace fir Value* IRBuilder::CreateValue(Type* t, const std::string& vname) { - return ConstantValue::getZeroValue(t); + auto ret = ConstantValue::getZeroValue(t); + ret->setName(vname); + + return ret; } @@ -1554,7 +1572,7 @@ namespace fir } - Value* IRBuilder::InsertValueByName(Value* val, std::string n, Value* elm, const std::string& vname) + Value* IRBuilder::InsertValueByName(Value* val, const std::string& n, Value* elm, const std::string& vname) { Type* t = val->getType(); if(!t->isStructType() && !t->isClassType()) @@ -1569,7 +1587,7 @@ namespace fir return this->addInstruction(_insertValue(val, ind, et, elm), vname); } - Value* IRBuilder::ExtractValueByName(Value* val, std::string n, const std::string& vname) + Value* IRBuilder::ExtractValueByName(Value* val, const std::string& n, const std::string& vname) { Type* t = val->getType(); if(!t->isStructType() && !t->isClassType()) @@ -1704,7 +1722,7 @@ namespace fir return this->ReadPtr(this->GetSAARefCountPointer(arr), vname); } - void IRBuilder::SetSAARefCount(Value* arr, Value* val, const std::string& vname) + void IRBuilder::SetSAARefCount(Value* arr, Value* val) { if(val->getType() != Type::getNativeWord()) error("irbuilder: val is not an int64"); @@ -1876,12 +1894,13 @@ namespace fir return this->ReadPtr(this->GetAnyRefCountPointer(arr), vname); } - void IRBuilder::SetAnyRefCount(Value* arr, Value* val, const std::string& vname) + void IRBuilder::SetAnyRefCount(Value* arr, Value* val) { if(val->getType() != Type::getNativeWord()) error("irbuilder: val is not an int64"); - this->WritePtr(val, this->PointerTypeCast(this->GetAnyRefCountPointer(arr), Type::getNativeWordPtr()->getMutablePointerVersion())); + this->WritePtr(val, this->PointerTypeCast(this->GetAnyRefCountPointer(arr), + Type::getNativeWordPtr()->getMutablePointerVersion())); } @@ -2216,7 +2235,7 @@ namespace fir this->addInstruction(make_instr(OpKind::Unreachable, true, Type::getVoid(), { }), ""); } - IRBlock* IRBuilder::addNewBlockInFunction(std::string name, Function* func) + IRBlock* IRBuilder::addNewBlockInFunction(const std::string& name, Function* func) { IRBlock* block = new IRBlock(func); if(func != this->currentFunction) @@ -2235,14 +2254,11 @@ namespace fir for(auto b : this->currentFunction->blocks) if(b->getName().str() == name) cnt++; - if(cnt > 0) - name += "." + std::to_string(cnt); - - block->setName(name); + block->setName(strprintf("%s%s", name, cnt > 0 ? strprintf(".%d", cnt) : "")); return block; } - IRBlock* IRBuilder::addNewBlockAfter(std::string name, IRBlock* block) + IRBlock* IRBuilder::addNewBlockAfter(const std::string& name, IRBlock* block) { IRBlock* nb = new IRBlock(block->parentFunction); if(nb->parentFunction != this->currentFunction) @@ -2265,10 +2281,8 @@ namespace fir for(auto bk : this->currentFunction->blocks) if(bk->getName().str() == name) cnt++; - if(cnt > 0) - name += "." + std::to_string(cnt); + nb->setName(strprintf("%s%s", name, cnt > 0 ? strprintf(".%d", cnt) : "")); - nb->setName(name); this->currentFunction->blocks.insert(this->currentFunction->blocks.begin() + i + 1, nb); return nb; } diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index 8d8f6998..3035bf69 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -9,7 +9,7 @@ namespace fir { - Module::Module(std::string nm) + Module::Module(const std::string& nm) { this->moduleName = nm; } @@ -222,11 +222,10 @@ namespace fir // todo: *very* inefficient. std::vector ret; - for(auto fn : this->functions) + for(const auto& [ ident, fn ] : this->functions) { - // if(fn.first.name == id.name && fn.first.scope == id.scope) - if(fn.first == id) - ret.push_back(fn.second); + if(ident == id) + ret.push_back(fn); } return ret; @@ -276,7 +275,7 @@ namespace fir - GlobalVariable* Module::createGlobalString(std::string str) + GlobalVariable* Module::createGlobalString(const std::string& str) { static int stringId = 0; @@ -308,12 +307,12 @@ namespace fir std::string ret; ret = "# MODULE = " + this->getModuleName() + "\n"; - for(auto string : this->globalStrings) + for(const auto& [ str, gv ] : this->globalStrings) { - ret += "global string (%" + std::to_string(string.second->id); + ret += "global string (%" + std::to_string(gv->id); std::string copy; - for(auto c : string.first) + for(auto c : str) { if(c == '\r') copy += "\\r"; else if(c == '\n') copy += "\\n"; @@ -321,7 +320,7 @@ namespace fir else copy += c; } - ret += ") [" + std::to_string(string.first.length()) + "] = \"" + copy + "\"\n"; + ret += ") [" + std::to_string(str.length()) + "] = \"" + copy + "\"\n"; } for(auto global : this->globals) @@ -342,9 +341,9 @@ namespace fir ret += "declare type :: " + type.second->str() + " { " + tl + " }\n"; } - for(auto fp : this->functions) + for(const auto& [ id, fp ] : this->functions) { - Function* ffn = fp.second; + Function* ffn = fp; std::string decl; @@ -394,7 +393,7 @@ namespace fir - Function* Module::getIntrinsicFunction(std::string id) + Function* Module::getIntrinsicFunction(const std::string& id) { Identifier name; FunctionType* ft = 0; @@ -457,7 +456,9 @@ namespace fir std::vector Module::getGlobalVariables() { std::vector ret; - for(auto g : this->globals) + ret.reserve(this->globals.size()); + + for(const auto& g : this->globals) ret.push_back(g.second); return ret; @@ -466,7 +467,9 @@ namespace fir std::vector Module::getNamedTypes() { std::vector ret; - for(auto g : this->namedTypes) + ret.reserve(this->namedTypes.size()); + + for(const auto& g : this->namedTypes) ret.push_back(g.second); return ret; @@ -475,7 +478,9 @@ namespace fir std::vector Module::getAllFunctions() { std::vector ret; - for(auto g : this->functions) + ret.reserve(this->functions.size()); + + for(const auto& g : this->functions) ret.push_back(g.second); return ret; @@ -486,7 +491,7 @@ namespace fir return this->moduleName; } - void Module::setModuleName(std::string name) + void Module::setModuleName(const std::string& name) { this->moduleName = name; } diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index f07c7dbc..1a09a5c0 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -108,13 +108,13 @@ namespace fir } } - for(auto p : members) + for(const auto& [ name, ty ] : members) { - this->classMembers[p.first] = p.second; - this->indexMap[p.first] = i; + this->classMembers[name] = ty; + this->indexMap[name] = i; - this->nameList.push_back(p.first); - this->typeList.push_back(p.second); + this->nameList.push_back(name); + this->typeList.push_back(ty); i++; } @@ -217,11 +217,13 @@ namespace fir return this->methodList; } - std::vector ClassType::getMethodsWithName(std::string id) + std::vector ClassType::getMethodsWithName(const std::string& id) { - std::vector ret; auto l = this->classMethodMap[id]; + std::vector ret; + ret.reserve(l.size()); + for(auto f : l) ret.push_back(f); @@ -370,7 +372,7 @@ namespace fir // check every member of the current mapping -- not the fastest method i admit. bool found = false; - for(auto vm : this->virtualMethodMap) + for(const auto& vm : this->virtualMethodMap) { if(vm.first.first == method->getName().name && _areTypeListsVirtuallyCompatible(vm.first.second, list)) diff --git a/source/fir/Types/SingleTypes.cpp b/source/fir/Types/SingleTypes.cpp index 4c3cd592..c1e295da 100644 --- a/source/fir/Types/SingleTypes.cpp +++ b/source/fir/Types/SingleTypes.cpp @@ -7,13 +7,15 @@ namespace fir { + using PolySubst = util::hash_map; + static AnyType* singleAny = 0; AnyType::AnyType() : Type(TypeKind::Any) { } std::string AnyType::str() { return "any"; } std::string AnyType::encodedStr() { return "any"; } bool AnyType::isTypeEqual(Type* other) { return other && other->isAnyType(); } AnyType* AnyType::get() { return singleAny = (singleAny ? singleAny : new AnyType()); } - fir::Type* AnyType::substitutePlaceholders(const util::hash_map& subst) { return this; } + fir::Type* AnyType::substitutePlaceholders(const PolySubst&) { return this; } static BoolType* singleBool = 0; @@ -22,7 +24,7 @@ namespace fir std::string BoolType::encodedStr() { return "bool"; } bool BoolType::isTypeEqual(Type* other) { return other && other->isBoolType(); } BoolType* BoolType::get() { return singleBool = (singleBool ? singleBool : new BoolType()); } - fir::Type* BoolType::substitutePlaceholders(const util::hash_map& subst) { return this; } + fir::Type* BoolType::substitutePlaceholders(const PolySubst&) { return this; } static VoidType* singleVoid = 0; @@ -31,7 +33,7 @@ namespace fir std::string VoidType::encodedStr() { return "void"; } bool VoidType::isTypeEqual(Type* other) { return other && other->isVoidType(); } VoidType* VoidType::get() { return singleVoid = (singleVoid ? singleVoid : new VoidType()); } - fir::Type* VoidType::substitutePlaceholders(const util::hash_map& subst) { return this; } + fir::Type* VoidType::substitutePlaceholders(const PolySubst&) { return this; } static NullType* singleNull = 0; @@ -40,7 +42,7 @@ namespace fir std::string NullType::encodedStr() { return "nulltype"; } bool NullType::isTypeEqual(Type* other) { return other && other->isNullType(); } NullType* NullType::get() { return singleNull = (singleNull ? singleNull : new NullType()); } - fir::Type* NullType::substitutePlaceholders(const util::hash_map& subst) { return this; } + fir::Type* NullType::substitutePlaceholders(const PolySubst&) { return this; } static RangeType* singleRange = 0; @@ -49,7 +51,7 @@ namespace fir std::string RangeType::encodedStr() { return "range"; } bool RangeType::isTypeEqual(Type* other) { return other && other->isRangeType(); } RangeType* RangeType::get() { return singleRange = (singleRange ? singleRange : new RangeType()); } - fir::Type* RangeType::substitutePlaceholders(const util::hash_map& subst) { return this; } + fir::Type* RangeType::substitutePlaceholders(const PolySubst&) { return this; } static StringType* singleString = 0; @@ -58,7 +60,7 @@ namespace fir std::string StringType::encodedStr() { return "string"; } bool StringType::isTypeEqual(Type* other) { return other && other->isStringType(); } StringType* StringType::get() { return singleString = (singleString ? singleString : new StringType()); } - fir::Type* StringType::substitutePlaceholders(const util::hash_map& subst) { return this; } + fir::Type* StringType::substitutePlaceholders(const PolySubst&) { return this; } @@ -86,7 +88,7 @@ namespace fir this->_signed = neg; this->_floating = flt; } - fir::Type* ConstantNumberType::substitutePlaceholders(const util::hash_map& subst) + fir::Type* ConstantNumberType::substitutePlaceholders(const PolySubst& subst) { return this; } @@ -181,7 +183,7 @@ namespace fir } - static fir::Type* _substitute(const util::hash_map& subst, fir::Type* t) + static fir::Type* _substitute(const PolySubst& subst, fir::Type* t) { if(auto it = subst.find(t); it != subst.end()) return it->second->substitutePlaceholders(subst); @@ -189,7 +191,7 @@ namespace fir return t; } - fir::Type* PolyPlaceholderType::substitutePlaceholders(const util::hash_map& subst) + fir::Type* PolyPlaceholderType::substitutePlaceholders(const PolySubst& subst) { return _substitute(subst, this); } diff --git a/source/fir/Types/StructType.cpp b/source/fir/Types/StructType.cpp index 307882f4..32845ee6 100644 --- a/source/fir/Types/StructType.cpp +++ b/source/fir/Types/StructType.cpp @@ -107,11 +107,11 @@ namespace fir void StructType::setBody(const std::vector>& members) { size_t i = 0; - for(auto p : members) + for(const auto& [ name, ty ] : members) { - this->structMembers[p.first] = p.second; - this->indexMap[p.first] = i; - this->typeList.push_back(p.second); + this->structMembers[name] = ty; + this->indexMap[name] = i; + this->typeList.push_back(ty); i++; } diff --git a/source/fir/Types/Type.cpp b/source/fir/Types/Type.cpp index b0a491e5..b39d5c9d 100644 --- a/source/fir/Types/Type.cpp +++ b/source/fir/Types/Type.cpp @@ -436,7 +436,7 @@ namespace fir else if(ty->isUnionType()) { bool res = false; - for(auto t : ty->toUnionType()->getVariants()) + for(const auto& t : ty->toUnionType()->getVariants()) res |= _containsPlaceholders(t.second, seen, found); return res; @@ -1119,7 +1119,7 @@ namespace fir auto ut = type->toRawUnionType(); size_t maxSz = 0; - for(auto v : ut->getVariants()) + for(const auto& v : ut->getVariants()) maxSz = std::max(maxSz, getSizeOfType(v.second)); iceAssert(maxSz > 0); diff --git a/source/fir/Value.cpp b/source/fir/Value.cpp index 42ebab43..ebe53f62 100644 --- a/source/fir/Value.cpp +++ b/source/fir/Value.cpp @@ -30,7 +30,7 @@ namespace fir this->ident = name; } - void Value::setName(std::string name) + void Value::setName(const std::string& name) { this->ident = Identifier(name, IdKind::Name); } diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index a9801476..277d2564 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -157,8 +157,17 @@ namespace interp if(target->dataSize != sz) error("interp: cannot set value, size mismatch (%d vs %d)", target->dataSize, sz); - if(sz > LARGE_DATA_SIZE) memmove(target->ptr, value, sz); - else memmove(&target->data[0], value, sz); + if(sz > LARGE_DATA_SIZE) + { + if(!target->ptr) + target->ptr = calloc(1, sz); + + memmove(target->ptr, value, sz); + } + else + { + memmove(&target->data[0], value, sz); + } } static void setValue(InterpState* is, interp::Value* target, const interp::Value& val) @@ -242,6 +251,8 @@ namespace interp const std::vector& inserts) -> interp::Value { std::vector vals; + vals.reserve(inserts.size()); + for(const auto& x : inserts) vals.push_back(makeConstant(is, x)); @@ -401,6 +412,8 @@ namespace interp } else { + iceAssert(c); + auto ret = is->makeValue(c); return (cachedConstants[c] = ret); } @@ -420,7 +433,7 @@ namespace interp void InterpState::initialise() { - for(const auto [ str, glob ] : this->module->_getGlobalStrings()) + for(const auto& [ str, glob ] : this->module->_getGlobalStrings()) { auto val = makeValue(glob); auto s = makeGlobalString(this, str); @@ -431,7 +444,7 @@ namespace interp this->globals[glob] = { val, false }; } - for(const auto [ id, glob ] : this->module->_getGlobals()) + for(const auto& [ id, glob ] : this->module->_getGlobals()) { auto ty = glob->getType(); auto sz = getSizeOfType(ty); @@ -455,14 +468,14 @@ namespace interp this->globals[glob] = { ret, false }; } - for(auto intr : this->module->_getIntrinsicFunctions()) + for(const auto& [ id, intr ] : this->module->_getIntrinsicFunctions()) { - auto fname = Identifier("__interp_intrinsic_" + intr.first.str(), IdKind::Name); - auto fn = this->module->getOrCreateFunction(fname, intr.second->getType(), fir::LinkageType::ExternalWeak); + auto fname = Identifier("__interp_intrinsic_" + id.str(), IdKind::Name); + auto fn = this->module->getOrCreateFunction(fname, intr->getType(), fir::LinkageType::ExternalWeak); // interp::compileFunction already maps the newly compiled interp::Function, but since we created a // new function here `fn` that doesn't match the intrinsic function `intr`, we need to map that as well. - this->compiledFunctions[intr.second] = this->compileFunction(fn); + this->compiledFunctions[intr] = this->compileFunction(fn); } @@ -999,7 +1012,7 @@ namespace interp void* ret_buffer = new uint8_t[std::max(ffi_retty->size, size_t(8))]; ffi_call(&fn_cif, reinterpret_cast(fnptr), ret_buffer, arg_pointers); - interp::Value ret; + interp::Value ret = { 0 }; ret.type = fnty->getReturnType(); ret.dataSize = ffi_retty->size; diff --git a/source/frontend/arguments.cpp b/source/frontend/arguments.cpp index 087477e7..d58588ce 100644 --- a/source/frontend/arguments.cpp +++ b/source/frontend/arguments.cpp @@ -107,7 +107,7 @@ static void printHelp() printf("options:\n"); size_t maxl = 0; - for(auto p : list) + for(const auto& p : list) { if(p.first.length() > maxl) maxl = p.first.length(); @@ -116,8 +116,8 @@ static void printHelp() maxl += 4; // ok - for(auto p : list) - printf(" %s%s%s\n", p.first.c_str(), std::string(maxl - p.first.length(), ' ').c_str(), p.second.c_str()); + for(const auto& [ opt, desc ] : list) + printf(" %s%s%s\n", opt.c_str(), std::string(maxl - opt.length(), ' ').c_str(), desc.c_str()); printf("\n"); } @@ -277,7 +277,7 @@ namespace frontend } - std::string getParameter(std::string name) + std::string getParameter(const std::string& name) { if(name == "mcmodel") return _mcModel; diff --git a/source/frontend/dependencies.cpp b/source/frontend/dependencies.cpp index 914bda94..77d28e1b 100644 --- a/source/frontend/dependencies.cpp +++ b/source/frontend/dependencies.cpp @@ -157,7 +157,7 @@ namespace frontend std::vector checkForCycles(const std::string& topmod, frontend::DependencyGraph* graph) { auto groups = graph->findCyclicDependencies(); - for(auto grp : groups) + for(const auto& grp : groups) { if(grp.size() > 1) { @@ -216,7 +216,7 @@ namespace frontend // get the proper import of each 'import' std::vector fullpaths; - for(auto imp : imports) + for(const auto& imp : imports) { auto tovisit = resolveImport(imp.name, imp.loc, full); graph->addModuleDependency(full, tovisit, imp); diff --git a/source/frontend/errors.cpp b/source/frontend/errors.cpp index 5d489395..c310bac1 100644 --- a/source/frontend/errors.cpp +++ b/source/frontend/errors.cpp @@ -53,7 +53,7 @@ static std::string fetchContextLine(Location loc, size_t* adjust) { if(loc.fileID == 0) return ""; - auto lines = frontend::getFileLines(loc.fileID); + const auto& lines = frontend::getFileLines(loc.fileID); if(lines.size() > loc.line) { std::stringstream ln; @@ -123,7 +123,7 @@ static std::string getSpannedContext(const Location& loc, const std::vector& getFileLines(size_t id) { - std::string fp = getFilenameFromID(id); - return readFileIfNecessary(fp).lines; + return readFileIfNecessary(getFilenameFromID(id)).lines; } const std::vector& getImportTokenLocationsForFile(const std::string& filename) @@ -210,7 +209,7 @@ namespace frontend std::string removeExtensionFromFilename(const std::string& name) { - auto i = name.find_last_of("."); + auto i = name.find_last_of('.'); return name.substr(0, i); } } diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index 2eeeeaf3..8c393725 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -30,8 +30,9 @@ namespace parser // first one - bool hadParen = false; { + bool hadParen = false; + if(st.front() == TT::LParen) hadParen = true, st.eat(); @@ -57,7 +58,6 @@ namespace parser st.eat(); } - hadParen = false; cases.back().body = parseBracedBlock(st); } diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index b5b8bb97..dea6909d 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -850,7 +850,7 @@ namespace parser return ret; } - static FunctionCall* parseFunctionCall(State& st, const Location& loc, std::string name) + static FunctionCall* parseFunctionCall(State& st, const Location& loc, const std::string& name) { auto ret = util::pool(loc, name); diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index d4a4d44a..4e26305f 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -296,7 +296,7 @@ namespace parser return root; } - ParsedFile parseFile(std::string filename, frontend::CollectorState& cs) + ParsedFile parseFile(const std::string& filename, frontend::CollectorState& cs) { auto full = frontend::getFullPathOfFile(filename); const TokenList& tokens = frontend::getFileTokens(full); diff --git a/source/include/ast.h b/source/include/ast.h index 14838b46..8bd94c86 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -22,7 +22,6 @@ namespace fir namespace sst { - struct TypeDefn; struct TypecheckState; struct FunctionDefn; struct FunctionDecl; @@ -532,7 +531,7 @@ namespace ast struct Ident : Expr { - Ident(const Location& l, std::string n) : Expr(l), name(n) { this->readableName = "identifier"; } + Ident(const Location& l, const std::string& n) : Expr(l), name(n) { this->readableName = "identifier"; } ~Ident() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -620,7 +619,8 @@ namespace ast struct BinaryOp : Expr { - BinaryOp(const Location& loc, std::string o, Expr* l, Expr* r) : Expr(loc), op(o), left(l), right(r) { this->readableName = "binary expression"; } + BinaryOp(const Location& loc, const std::string& o, Expr* l, Expr* r) + : Expr(loc), op(o), left(l), right(r) { this->readableName = "binary expression"; } ~BinaryOp() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -702,7 +702,7 @@ namespace ast struct FunctionCall : Expr { - FunctionCall(const Location& l, std::string n) : Expr(l), name(n) { this->readableName = "function call"; } + FunctionCall(const Location& l, const std::string& n) : Expr(l), name(n) { this->readableName = "function call"; } ~FunctionCall() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -747,7 +747,7 @@ namespace ast struct LitNumber : Expr { - LitNumber(const Location& l, std::string n) : Expr(l), num(n) { this->readableName = "number literal"; } + LitNumber(const Location& l, const std::string& n) : Expr(l), num(n) { this->readableName = "number literal"; } ~LitNumber() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -777,7 +777,9 @@ namespace ast struct LitString : Expr { - LitString(const Location& l, std::string s, bool isc) : Expr(l), str(s), isCString(isc) { this->readableName = "string literal"; } + LitString(const Location& l, const std::string& s, bool isc) + : Expr(l), str(s), isCString(isc) { this->readableName = "string literal"; } + ~LitString() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -796,7 +798,7 @@ namespace ast struct LitTuple : Expr { - LitTuple(const Location& l, std::vector its) : Expr(l), values(its) { this->readableName = "tuple literal"; } + LitTuple(const Location& l, const std::vector& its) : Expr(l), values(its) { this->readableName = "tuple literal"; } ~LitTuple() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -806,7 +808,7 @@ namespace ast struct LitArray : Expr { - LitArray(const Location& l, std::vector its) : Expr(l), values(its) { this->readableName = "array literal"; } + LitArray(const Location& l, const std::vector& its) : Expr(l), values(its) { this->readableName = "array literal"; } ~LitArray() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; @@ -845,7 +847,7 @@ namespace ast struct TopLevelBlock : Stmt { - TopLevelBlock(const Location& l, std::string n) : Stmt(l), name(n) { this->readableName = "namespace"; } + TopLevelBlock(const Location& l, const std::string& n) : Stmt(l), name(n) { this->readableName = "namespace"; } ~TopLevelBlock() { } virtual TCResult typecheck(sst::TypecheckState* fs, fir::Type* infer = 0) override; diff --git a/source/include/backend.h b/source/include/backend.h index ef74cba1..022fc939 100644 --- a/source/include/backend.h +++ b/source/include/backend.h @@ -70,7 +70,8 @@ namespace backend BackendCaps::Capabilities getCapabilities() { return static_cast(this->capabilities); } bool hasCapability(BackendCaps::Capabilities cap) { return this->capabilities & cap; } - static Backend* getBackendFromOption(BackendOption opt, CompiledData& cd, std::vector in, std::string out); + static Backend* getBackendFromOption(BackendOption opt, CompiledData& cd, const std::vector& in, + const std::string& out); virtual ~Backend() { } @@ -82,7 +83,7 @@ namespace backend virtual std::string str() = 0; protected: - Backend(int caps, CompiledData& dat, std::vector inputs, std::string output) + Backend(int caps, CompiledData& dat, const std::vector& inputs, const std::string& output) : capabilities(caps), compiledData(dat), inputFilenames(inputs), outputFilename(output) { } int capabilities; @@ -93,7 +94,7 @@ namespace backend struct x64Backend : Backend { - x64Backend(CompiledData& dat, std::vector inputs, std::string output); + x64Backend(CompiledData& dat, const std::vector& inputs, const std::string& output); virtual ~x64Backend() { } virtual void performCompilation() override; diff --git a/source/include/backends/interp.h b/source/include/backends/interp.h index f1c6830d..715b169b 100644 --- a/source/include/backends/interp.h +++ b/source/include/backends/interp.h @@ -14,7 +14,7 @@ namespace backend { struct FIRInterpBackend : Backend { - FIRInterpBackend(CompiledData& dat, std::vector inputs, std::string output); + FIRInterpBackend(CompiledData& dat, const std::vector& inputs, const std::string& output); virtual ~FIRInterpBackend(); virtual void performCompilation() override; diff --git a/source/include/backends/llvm.h b/source/include/backends/llvm.h index 97a4d1f4..2905bf72 100644 --- a/source/include/backends/llvm.h +++ b/source/include/backends/llvm.h @@ -93,7 +93,7 @@ namespace backend struct LLVMBackend : Backend { - LLVMBackend(CompiledData& dat, std::vector inputs, std::string output); + LLVMBackend(CompiledData& dat, const std::vector& inputs, const std::string& output); virtual ~LLVMBackend() { } virtual void performCompilation() override; diff --git a/source/include/container.h b/source/include/container.h index c41beedb..2119684d 100644 --- a/source/include/container.h +++ b/source/include/container.h @@ -46,7 +46,7 @@ namespace util return *this; } - FastInsertVector(FastInsertVector&& other) + FastInsertVector(FastInsertVector&& other) noexcept { // move. this->chunks = std::move(other.chunks); @@ -56,7 +56,7 @@ namespace util other.length = 0; } - FastInsertVector& operator = (FastInsertVector&& other) + FastInsertVector& operator = (FastInsertVector&& other) noexcept { if(this != &other) { @@ -158,12 +158,12 @@ namespace util return *this; } - MemoryPool(MemoryPool&& other) + MemoryPool(MemoryPool&& other) noexcept { this->storage = std::move(other.storage); } - MemoryPool& operator = (MemoryPool&& other) + MemoryPool& operator = (MemoryPool&& other) noexcept { if(this != &other) this->storage = std::move(other.storage); return *this; diff --git a/source/include/defs.h b/source/include/defs.h index 3a91ded4..cb831bd0 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -28,7 +28,7 @@ namespace zpr (std::is_pointer_v && std::is_base_of_v>) >::type> { - std::string print(const T& x, const format_args& args) + std::string print(const T& x, const format_args&) { return x->str(); } @@ -73,14 +73,13 @@ std::string strprintf(const char* fmt, Ts&&... ts) #define __nothing #ifdef NDEBUG -#define iceAssert(x) ((void) (x)) +#define iceAssert(x) ((void) (x)) #else -#define iceAssert(x) ((x) ? ((void) (0)) : _error_and_exit("compiler assertion at %s:%d, cause:\n'%s' evaluated to false\n", __FILE__, __LINE__, #x)) +#define iceAssert(x) ((x) ? ((void) (0)) : _error_and_exit("compiler assertion at %s:%d, cause:\n'%s' evaluated to false\n", __FILE__, __LINE__, #x)) #endif -#define TAB_WIDTH 4 - -#define dcast(t, v) dynamic_cast(v) +#define TAB_WIDTH 4 +#define dcast(t, v) (dynamic_cast(v)) namespace util { @@ -138,7 +137,7 @@ template std::string strbold(const char* fmt, Ts&&... ts) struct Identifier { Identifier() : name(""), kind(IdKind::Invalid) { } - Identifier(std::string n, IdKind k) : name(n), kind(k) { } + Identifier(const std::string& n, IdKind k) : name(n), kind(k) { } std::string name; std::vector scope; @@ -460,7 +459,7 @@ struct TCResult else if(this->isDefn()) this->_df = r._df; } - TCResult(TCResult&& r) + TCResult(TCResult&& r) noexcept { this->_kind = r._kind; @@ -477,7 +476,7 @@ struct TCResult return *this; } - TCResult& operator = (TCResult&& r) + TCResult& operator = (TCResult&& r) noexcept { if(&r != this) { diff --git a/source/include/errors.h b/source/include/errors.h index c19e6887..ed413bf8 100644 --- a/source/include/errors.h +++ b/source/include/errors.h @@ -77,9 +77,6 @@ inline void debuglogln(const char* s, Ts&&... ts) -namespace ast { struct Expr; } -namespace sst { struct Expr; } - namespace frontend { const std::string& getFilenameFromID(size_t fileID); diff --git a/source/include/frontend.h b/source/include/frontend.h index 82e62d21..b088c2df 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -13,10 +13,6 @@ #include #include -namespace ast -{ - struct Expr; -} namespace sst { @@ -37,7 +33,7 @@ namespace backend namespace frontend { - std::string getParameter(std::string arg); + std::string getParameter(const std::string& arg); backend::ProgOutputMode getOutputMode(); backend::OptimisationLevel getOptLevel(); diff --git a/source/include/gluecode.h b/source/include/gluecode.h index ae41ffbe..46e235a4 100644 --- a/source/include/gluecode.h +++ b/source/include/gluecode.h @@ -51,7 +51,7 @@ namespace cgn namespace glue { - void printRuntimeError(CodegenState* cs, fir::Value* pos, std::string msg, std::vector args); + void printRuntimeError(CodegenState* cs, fir::Value* pos, const std::string& msg, const std::vector& args); namespace string { diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 9cea6cfe..30e05a59 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -120,12 +120,12 @@ namespace fir { friend struct Module; - static ConstantArray* get(Type* type, std::vector vals); + static ConstantArray* get(Type* type, const std::vector& vals); std::vector getValues() { return this->values; } protected: - ConstantArray(Type* type, std::vector vals); + ConstantArray(Type* type, const std::vector& vals); std::vector values; }; @@ -134,10 +134,10 @@ namespace fir { friend struct Module; - static ConstantStruct* get(StructType* st, std::vector members); + static ConstantStruct* get(StructType* st, const std::vector& members); protected: - ConstantStruct(StructType* st, std::vector members); + ConstantStruct(StructType* st, const std::vector& members); std::vector members; }; @@ -161,11 +161,11 @@ namespace fir { friend struct Module; - static ConstantCharSlice* get(std::string value); + static ConstantCharSlice* get(const std::string& value); std::string getValue(); protected: - ConstantCharSlice(std::string str); + ConstantCharSlice(const std::string& str); std::string str; }; @@ -174,11 +174,11 @@ namespace fir { friend struct Module; - static ConstantTuple* get(std::vector mems); + static ConstantTuple* get(const std::vector& mems); std::vector getValues(); protected: - ConstantTuple(std::vector mems); + ConstantTuple(const std::vector& mems); std::vector values; }; diff --git a/source/include/ir/irbuilder.h b/source/include/ir/irbuilder.h index edf0624a..335847bd 100644 --- a/source/include/ir/irbuilder.h +++ b/source/include/ir/irbuilder.h @@ -113,7 +113,7 @@ namespace fir Value* GEP2(Value* ptr, Value* ptrIndex, Value* elmIndex, const std::string& vname = ""); Value* ConstGEP2(Value* ptr, size_t ptrIndex, size_t elmIndex, const std::string& vname = ""); - void SetVtable(Value* ptr, Value* table, const std::string& vname = ""); + void SetVtable(Value* ptr, Value* table); void CondBranch(Value* condition, IRBlock* trueBlock, IRBlock* falseBlock); void UnCondBranch(IRBlock* target); @@ -123,14 +123,14 @@ namespace fir Value* Select(Value* cond, Value* one, Value* two, const std::string& vname = ""); - Value* BinaryOp(std::string ao, Value* a, Value* b, const std::string& vname = ""); + Value* BinaryOp(const std::string& ao, Value* a, Value* b, const std::string& vname = ""); Value* CreateValue(Type* t, const std::string& vname = ""); Value* ExtractValue(Value* val, const std::vector& inds, const std::string& vname = ""); - Value* ExtractValueByName(Value* val, std::string mem, const std::string& vname = ""); + Value* ExtractValueByName(Value* val, const std::string& mem, const std::string& vname = ""); - [[nodiscard]] Value* InsertValueByName(Value* val, std::string mem, Value* elm, const std::string& vname = ""); + [[nodiscard]] Value* InsertValueByName(Value* val, const std::string& mem, Value* elm, const std::string& vname = ""); [[nodiscard]] Value* InsertValue(Value* val, const std::vector& inds, Value* elm, const std::string& vname = ""); @@ -148,7 +148,7 @@ namespace fir [[nodiscard]] Value* SetSAACapacity(Value* str, Value* val, const std::string& vname = ""); [[nodiscard]] Value* SetSAALength(Value* str, Value* val, const std::string& vname = ""); [[nodiscard]] Value* SetSAAData(Value* str, Value* val, const std::string& vname = ""); - void SetSAARefCount(Value* str, Value* val, const std::string& vname = ""); + void SetSAARefCount(Value* str, Value* val); Value* CreateSliceFromSAA(Value* str, bool mut, const std::string& vname = ""); @@ -168,7 +168,7 @@ namespace fir Value* GetAnyRefCount(Value* str, const std::string& vname = ""); Value* GetAnyRefCountPointer(Value* any, const std::string& vname = ""); - void SetAnyRefCount(Value* str, Value* val, const std::string& vname = ""); + void SetAnyRefCount(Value* str, Value* val); [[nodiscard]] Value* SetAnyData(Value* any, Value* val, const std::string& vname = ""); [[nodiscard]] Value* SetAnyTypeID(Value* any, Value* val, const std::string& vname = ""); [[nodiscard]] Value* SetAnyRefCountPointer(Value* str, Value* val, const std::string& vname = ""); @@ -217,8 +217,8 @@ namespace fir void Unreachable(); - IRBlock* addNewBlockInFunction(std::string name, Function* func); - IRBlock* addNewBlockAfter(std::string name, IRBlock* block); + IRBlock* addNewBlockInFunction(const std::string& name, Function* func); + IRBlock* addNewBlockAfter(const std::string& name, IRBlock* block); void setCurrentBlock(IRBlock* block); diff --git a/source/include/ir/module.h b/source/include/ir/module.h index 56e614c1..10d502fa 100644 --- a/source/include/ir/module.h +++ b/source/include/ir/module.h @@ -11,11 +11,6 @@ #include "value.h" #include "function.h" -namespace llvm -{ - class Module; -} - namespace fir { @@ -24,7 +19,7 @@ namespace fir friend struct GlobalValue; friend struct GlobalVariable; - Module(std::string nm); + Module(const std::string& nm); GlobalVariable* createGlobalVariable(const Identifier& id, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage); GlobalVariable* createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage); @@ -34,7 +29,7 @@ namespace fir GlobalVariable* getOrCreateVirtualTableForClass(ClassType* cls); - GlobalVariable* createGlobalString(std::string str); + GlobalVariable* createGlobalString(const std::string& str); std::vector getGlobalVariables(); std::vector getAllFunctions(); @@ -42,7 +37,7 @@ namespace fir // note: only looks at the name + scope, excludes the parameter list. std::vector getFunctionsWithName(const Identifier& id); - Function* getIntrinsicFunction(std::string id); + Function* getIntrinsicFunction(const std::string& id); Type* getNamedType(const Identifier& name); void addNamedType(const Identifier& name, Type* type); @@ -55,7 +50,7 @@ namespace fir Function* getOrCreateFunction(const Identifier& id, FunctionType* ftype, LinkageType linkage); std::string getModuleName(); - void setModuleName(std::string name); + void setModuleName(const std::string& name); std::string print(); diff --git a/source/include/ir/type.h b/source/include/ir/type.h index 67418577..b0c8b564 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -737,7 +737,7 @@ namespace fir const util::hash_map& getElementNameMap(); const std::vector& getMethods(); - std::vector getMethodsWithName(std::string id); + std::vector getMethodsWithName(const std::string& id); Function* getMethodWithType(FunctionType* ftype); const std::vector& getInitialiserFunctions(); diff --git a/source/include/ir/value.h b/source/include/ir/value.h index ba08770f..b4a60123 100644 --- a/source/include/ir/value.h +++ b/source/include/ir/value.h @@ -65,7 +65,7 @@ namespace fir // methods void setName(const Identifier& idt); - void setName(std::string s); + void setName(const std::string& s); const Identifier& getName(); bool hasName(); diff --git a/source/include/parser.h b/source/include/parser.h index b13e693e..7dfa3bb0 100644 --- a/source/include/parser.h +++ b/source/include/parser.h @@ -46,7 +46,7 @@ namespace parser size_t parseOperatorDecl(const lexer::TokenList& tokens, size_t i, int* kind, CustomOperatorDecl* out); std::vector parseImports(const std::string& filename, const lexer::TokenList& tokens); - ParsedFile parseFile(std::string filename, frontend::CollectorState& cs); + ParsedFile parseFile(const std::string& filename, frontend::CollectorState& cs); } diff --git a/source/include/pts.h b/source/include/pts.h index fb0e163a..e8ef20eb 100644 --- a/source/include/pts.h +++ b/source/include/pts.h @@ -7,11 +7,6 @@ #include "defs.h" #include "precompile.h" -namespace ast -{ - struct Expr; -} - // Parser Type System // why in the ever-living fuck does this even exist? // type system upon type system... diff --git a/source/include/sst.h b/source/include/sst.h index 22560ac1..a0031e05 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -76,7 +76,7 @@ namespace sst RawValueExpr(const Location& l, fir::Type* t) : Expr(l, t) { this->readableName = ""; } ~RawValueExpr() { } - virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override { return this->rawValue; } + virtual CGResult _codegen(cgn::CodegenState*, fir::Type* = 0) override { return this->rawValue; } CGResult rawValue; }; @@ -692,7 +692,7 @@ namespace sst ~StructFieldDefn() { } virtual std::string getKind() override { return "field"; } - virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override { return CGResult(0); } + virtual CGResult _codegen(cgn::CodegenState*, fir::Type* = 0) override { return CGResult(0); } TypeDefn* parentType = 0; bool isTransparentField = false; diff --git a/source/include/typecheck.h b/source/include/typecheck.h index d56d1f51..cc67d966 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -31,7 +31,6 @@ namespace ast struct Stmt; struct TypeDefn; struct FuncDefn; - struct FunctionCall; struct Parameterisable; } diff --git a/source/include/utils.h b/source/include/utils.h index 2a0d7b57..d46d9fb6 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -39,7 +39,7 @@ std::vector operator + (const std::vector& a, const std::vector& b) namespace util { template - bool match(const T& first) + bool match(const T&) { return true; } @@ -56,16 +56,10 @@ namespace util return (first == second) || match(first, comps...); } - template - std::vector merge(const std::vector& x) - { - return x; - } - template std::vector merge(const std::vector& x, const Args&... xs) { - return x + merge(xs...); + return (x + ... + xs); } @@ -98,7 +92,7 @@ namespace util template ::type> std::vector map(const std::vector& input, UnaryOp fn) { - std::vector ret; + std::vector ret; ret.reserve(input.size()); for(const auto& i : input) ret.push_back(fn(i)); @@ -123,7 +117,7 @@ namespace util template ::type> std::vector mapidx(const std::vector& input, UnaryOp fn) { - std::vector ret; + std::vector ret; ret.reserve(input.size()); for(size_t i = 0; i < input.size(); i++) ret.push_back(fn(input[i], i)); diff --git a/source/include/zpr.h b/source/include/zpr.h index 60989fd8..a10e43e5 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -184,21 +184,21 @@ namespace zpr // we need bogus ones that don't take the arguments. if we get error handling, these will throw errors. template - std::string _consume_both_sprint(const format_args& args, const char* fmt, Args&&... xs) + std::string _consume_both_sprint(const format_args&, const char* fmt, Args&&... xs) { return std::string("") .append(sprint(fmt, xs...)); } template - std::string _consume_prec_sprint(const format_args& args, const char* fmt, Args&&... xs) + std::string _consume_prec_sprint(const format_args&, const char* fmt, Args&&... xs) { return std::string("") .append(sprint(fmt, xs...)); } template - std::string _consume_width_sprint(const format_args& args, const char* fmt, Args&&... xs) + std::string _consume_width_sprint(const format_args&, const char* fmt, Args&&... xs) { return std::string("") .append(sprint(fmt, xs...)); @@ -317,7 +317,9 @@ namespace zpr if constexpr (!std::is_enum_v && std::is_signed_v) { is_neg = (x < 0); - if(is_neg) x = -x; + + if(is_neg) + x = -x; } std::string digits; diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 4a23ea5c..fef5f1f3 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -118,7 +118,7 @@ bool Identifier::operator != (const Identifier& other) const std::string Identifier::str() const { std::string ret; - for(auto s : this->scope) + for(const auto& s : this->scope) ret += s + "."; ret += this->name; @@ -126,7 +126,7 @@ std::string Identifier::str() const if(this->kind == IdKind::Function) { ret += "("; - for(auto p : this->params) + for(const auto& p : this->params) ret += p->str() + ", "; if(this->params.size() > 0) @@ -144,7 +144,7 @@ static std::string mangleScopeOnly(const Identifier& id) { bool first = true; std::string ret; - for(auto s : id.scope) + for(const auto& s : id.scope) { ret += (!first ? std::to_string(s.length()) : "") + s; first = false; @@ -153,7 +153,7 @@ static std::string mangleScopeOnly(const Identifier& id) return ret; } -static inline std::string lentypestr(std::string s) +static inline std::string lentypestr(const std::string& s) { return std::to_string(s.length()) + s; } @@ -356,12 +356,12 @@ namespace util namespace zpr { - std::string print_formatter::print(const Identifier& x, const format_args& args) + std::string print_formatter::print(const Identifier& x, const format_args&) { return x.str(); } - std::string print_formatter::print(const VisibilityLevel& x, const format_args& args) + std::string print_formatter::print(const VisibilityLevel& x, const format_args&) { switch(x) { @@ -375,31 +375,5 @@ namespace zpr } -// namespace tinyformat -// { -// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const VisibilityLevel& vl) -// { -// switch(vl) -// { -// case VisibilityLevel::Invalid: out << "invalid"; break; -// case VisibilityLevel::Public: out << "public"; break; -// case VisibilityLevel::Private: out << "private"; break; -// case VisibilityLevel::Internal: out << "internal"; break; -// } -// } - -// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const Identifier& id) -// { -// out << id.str(); -// } - -// void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty) -// { -// out << ty->str(); -// } -// } - - - From e83e81c86f5e72c75c87b4256c1a17593366bf79 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 25 Oct 2019 10:59:11 +0800 Subject: [PATCH 047/129] trait-based raii appears to work (for move/copy/kill) --- build/ultratiny.flx | 9 +-- meson.build | 1 + source/codegen/misc.cpp | 4 +- source/codegen/raii.cpp | 60 ++++++++--------- source/fir/Types/ClassType.cpp | 86 ++++++------------------ source/fir/Types/StructType.cpp | 19 ++++++ source/fir/Types/TypeUtils.cpp | 113 ++++++++++++++++++++++++++++++++ source/include/ir/type.h | 28 +++++--- source/include/utils.h | 6 ++ source/typecheck/classes.cpp | 13 ++-- source/typecheck/structs.cpp | 1 + 11 files changed, 219 insertions(+), 121 deletions(-) create mode 100644 source/fir/Types/TypeUtils.cpp diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 51815f74..383473c7 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -25,11 +25,14 @@ struct Foo : Drop, Copy, Move fn copy(other: &self) { printf("am copy\n") + this.data = other.data } fn move(other: &mut self) { printf("be move\n") + this.data = other.data + other.data = 0 } } @@ -66,12 +69,6 @@ fn two(x: Foo) /* raii traits checklist: - // 1. call the appropriate methods on structs. - (kind of: still need Copy and Move) - - // 2. generalise the raii mechanism to by searching for traits instead of hardcoding for classes - (kind of: still need Copy and Move) - 3. remove the copy/move/destruct stuff from FIR? */ diff --git a/meson.build b/meson.build index 9c84678b..89383f72 100644 --- a/meson.build +++ b/meson.build @@ -318,6 +318,7 @@ source_files = files([ 'source/fir/Types/SingleTypes.cpp', 'source/fir/Types/OpaqueType.cpp', 'source/fir/Types/StructType.cpp', + 'source/fir/Types/TypeUtils.cpp', 'source/fir/Types/ArrayType.cpp', 'source/fir/Types/TraitType.cpp', 'source/fir/Types/ClassType.cpp', diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index cc336adf..dd8dcb59 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -54,8 +54,8 @@ sst::FunctionDefn* cgn::CodegenState::findMatchingMethodInType(sst::TypeDefn* td //* i think this check should work, `areMethodsVirtuallyCompatible` basically checks the parameters but takes //* co/contravariance into account and doesn't match the first (self) parameter. - return (fn->id.name == method->id.name && fir::ClassType::areMethodsVirtuallyCompatible( - fn->type->toFunctionType(), method->type->toFunctionType()) + return (fn->id.name == method->id.name && fir::areMethodsVirtuallyCompatible( + fn->type->toFunctionType(), method->type->toFunctionType(), /* checking trait: */ true) ); }); diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index 56cf7945..bfa943eb 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -4,7 +4,6 @@ #include "sst.h" #include "codegen.h" -// #include "gluecode.h" #include "string_consts.h" namespace cgn @@ -173,24 +172,6 @@ namespace cgn - static fir::ClassType* doChecks(CodegenState* cs, fir::Value* from, fir::Value* target) - { - // this cleans up the callsites so we can just unconditionally call this. - if(!from->getType()->isClassType()) - { - cs->irb.Store(from, target); - return 0; - } - - auto clsty = from->getType()->toClassType(); - - // TODO: this is a shitty error message. - if(!target->islvalue()) - error(cs->loc(), "invalid operation on non-lvalue"); - - return clsty; - } - fir::Value* CodegenState::copyRAIIValue(fir::Value* value) { @@ -232,8 +213,9 @@ namespace cgn // if there is a copy-constructor, then we will call the copy constructor. if(auto copycon = clsty->getCopyConstructor(); copycon) { + // note: we make otherptr immutable, because copy() isn't supposed to pass the thing mutably! auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); - auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, false); this->irb.Call(copycon, selfptr, otherptr); } @@ -244,8 +226,9 @@ namespace cgn } else { + // note: we make otherptr immutable, because copy() isn't supposed to pass the thing mutably! auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); - auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, false); // well we got here, so we know at least the type has a copy constructor somewhere. auto copycon = getImplementationForRAIITrait(this, strs::names::support::RAII_TRAIT_COPY, from->getType()); @@ -263,26 +246,43 @@ namespace cgn { iceAssert(from->getType() == target->getType()); - auto clsty = doChecks(this, from, target); - if(!clsty) return; - - if(from->islvalue()) + if(!typeHasMoveConstructor(from->getType()) || from->islvalue()) { // you can't move from lvalues! this->copyRAIIValue(from, target); return; } - if(auto movecon = clsty->getMoveConstructor(); movecon) + if(from->getType()->isClassType()) { - auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); - auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + auto clsty = from->getType()->toClassType(); - this->irb.Call(movecon, selfptr, otherptr); + // if there is a copy-constructor, then we will call the copy constructor. + if(auto movecon = clsty->getMoveConstructor(); movecon) + { + // note: here both are mutable. + auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); + auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + + this->irb.Call(movecon, selfptr, otherptr); + } + else + { + doMemberWiseStuffIfNecessary(this, clsty, from, target, /* move: */ true); + } } else { - doMemberWiseStuffIfNecessary(this, clsty, from, target, /* move: */ true); + // note: here both are mutable. + auto selfptr = getAddressOfOrMakeTemporaryLValue(this, target, true); + auto otherptr = getAddressOfOrMakeTemporaryLValue(this, from, true); + + // well we got here, so we know at least the type has a copy constructor somewhere. + auto movecon = getImplementationForRAIITrait(this, strs::names::support::RAII_TRAIT_MOVE, from->getType()); + + // again, typechecking would have complained prior to this + iceAssert(movecon); + this->irb.Call(movecon, selfptr, otherptr); } this->removeRAIIValue(from); diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 1a09a5c0..21b0cbb3 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -6,8 +6,6 @@ #include "ir/type.h" #include "ir/function.h" -#include "pts.h" - namespace fir { // structs @@ -279,6 +277,25 @@ namespace fir } + void ClassType::addTraitImpl(TraitType* trt) + { + if(util::contains(this->implTraits, trt)) + error("'%s' already implements trait '%s'", this, trt); + + this->implTraits.push_back(trt); + } + + bool ClassType::implementsTrait(TraitType* trt) + { + return util::contains(this->implTraits, trt); + } + + std::vector ClassType::getImplementedTraits() + { + return this->implTraits; + } + + ClassType* ClassType::getBaseClass() { return this->baseClass; @@ -297,68 +314,6 @@ namespace fir } - // expects the self param to be removed already!!! - // note: this one doesn't check if the return types are compatible; we expect typechecking to have already - // verified that, and we don't store the return type in the class virtual method map anyway. - static bool _areTypeListsVirtuallyCompatible(const std::vector& base, const std::vector& fn) - { - // parameters must be contravariant, ie. fn must take more general types than base - // return type must be covariant, ie. fn must return a more specific type than base. - - // duh - if(base.size() != fn.size()) - return false; - - // drop the first argument. - for(auto [ base, derv ] : util::zip(base, fn)) - { - if(base == derv) - continue; - - if(!derv->isPointerType() || !derv->getPointerElementType()->isClassType() - || !base->isPointerType() || !base->getPointerElementType()->isClassType()) - { - return false; - } - - auto bce = base->getPointerElementType(); - auto dce = derv->getPointerElementType(); - - if(bce->isClassType() && dce->isClassType() && !bce->toClassType()->hasParent(dce)) - return false; - } - - return true; - } - - bool ClassType::areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn) - { - bool ret = _areTypeListsVirtuallyCompatible(util::drop(base->getArgumentTypes(), 1), util::drop(fn->getArgumentTypes(), 1)); - - if(!ret) - return false; - - auto baseRet = base->getReturnType(); - auto fnRet = fn->getReturnType(); - - // ok now check the return type. - if(baseRet == fnRet) - return true; - - if(baseRet->isPointerType() && baseRet->getPointerElementType()->isClassType() - && fnRet->isPointerType() && fnRet->getPointerElementType()->isClassType()) - { - auto br = baseRet->getPointerElementType()->toClassType(); - auto dr = fnRet->getPointerElementType()->toClassType(); - - return dr->hasParent(br); - } - else - { - return false; - } - } - void ClassType::addVirtualMethod(Function* method) { //* note: the 'reverse' virtual method map is to allow us, at translation time, to easily create the vtable without @@ -374,8 +329,7 @@ namespace fir bool found = false; for(const auto& vm : this->virtualMethodMap) { - if(vm.first.first == method->getName().name - && _areTypeListsVirtuallyCompatible(vm.first.second, list)) + if(vm.first.first == method->getName().name && areTypeListsContravariant(vm.first.second, list, /* trait checking: */ false)) { found = true; this->virtualMethodMap[{ method->getName().name, list }] = vm.second; diff --git a/source/fir/Types/StructType.cpp b/source/fir/Types/StructType.cpp index 32845ee6..1e9c5c07 100644 --- a/source/fir/Types/StructType.cpp +++ b/source/fir/Types/StructType.cpp @@ -103,6 +103,25 @@ namespace fir return this->typeList; } + void StructType::addTraitImpl(TraitType* trt) + { + if(util::contains(this->implTraits, trt)) + error("'%s' already implements trait '%s'", this, trt); + + this->implTraits.push_back(trt); + } + + bool StructType::implementsTrait(TraitType* trt) + { + return util::contains(this->implTraits, trt); + } + + std::vector StructType::getImplementedTraits() + { + return this->implTraits; + } + + void StructType::setBody(const std::vector>& members) { diff --git a/source/fir/Types/TypeUtils.cpp b/source/fir/Types/TypeUtils.cpp new file mode 100644 index 00000000..a1abf221 --- /dev/null +++ b/source/fir/Types/TypeUtils.cpp @@ -0,0 +1,113 @@ +// TypeUtils.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "errors.h" +#include "ir/type.h" +#include "ir/function.h" + +namespace fir +{ + static bool _checkTypeVariance(Type* base, Type* derv, bool contra, bool trait) + { + if(base == derv) + return true; + + // for now, we only support inheritance and stuff on pointers. + if(!base->isPointerType() || !derv->isPointerType()) + return false; + + auto baseelm = base->getPointerElementType(); + auto dervelm = derv->getPointerElementType(); + + if(baseelm->isClassType() && dervelm->isClassType()) + { + // if contravariant, then derv must be more general than base. + if(contra) return baseelm->toClassType()->hasParent(dervelm); + else return dervelm->toClassType()->hasParent(baseelm); + } + else + { + // if contra, check if base implements derv as a trait + // else check if derv implements base as a trait. of course, if the thing that + // is supposed to be a trait isn't a trait, it doesn't work. + if(contra) + { + // if contra *BUT* we are checking traits, then we should allow the case where the + // derived type is covariant (ie. implements the trait!) + + if(trait) + { + // TODO: this might not be correct? + std::swap(baseelm, dervelm); + } + + if(!dervelm->isTraitType()) + return false; + + return (baseelm->isStructType() && baseelm->toStructType()->implementsTrait(dervelm->toTraitType())) + || (dervelm->isStructType() && dervelm->toStructType()->implementsTrait(baseelm->toTraitType())); + } + else + { + if(!baseelm->isTraitType()) + return false; + + return (dervelm->isStructType() && dervelm->toStructType()->implementsTrait(baseelm->toTraitType())) + || (baseelm->isStructType() && baseelm->toStructType()->implementsTrait(dervelm->toTraitType())); + } + } + } + + bool areTypesCovariant(Type* base, Type* derv) + { + return _checkTypeVariance(base, derv, false, false); + } + + bool areTypesContravariant(Type* base, Type* derv, bool traitChecking) + { + return _checkTypeVariance(base, derv, true, traitChecking); + } + + bool areTypeListsContravariant(const std::vector& base, const std::vector& derv, bool traitChecking) + { + // parameters must be contravariant, ie. fn must take more general types than base + // return type must be covariant, ie. fn must return a more specific type than base. + + // duh + if(base.size() != derv.size()) + return false; + + // drop the first argument. + for(auto [ b, d ] : util::zip(base, derv)) + { + if(!areTypesContravariant(b, d, traitChecking)) + return false; + } + + return true; + } + + bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn, bool traitChecking) + { + debuglogln("check %s, %s", base, fn); + bool ret = areTypeListsContravariant(util::drop(base->getArgumentTypes(), 1), + util::drop(fn->getArgumentTypes(), 1), traitChecking); + + if(!ret) + return false; + + auto baseRet = base->getReturnType(); + auto fnRet = fn->getReturnType(); + + // ok now check the return type. + return areTypesCovariant(baseRet, fnRet); + } +} + + + + + + + diff --git a/source/include/ir/type.h b/source/include/ir/type.h index b0c8b564..236a9735 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -52,6 +52,16 @@ namespace fir void setNativeWordSizeInBits(size_t sz); size_t getNativeWordSizeInBits(); + // in theory. + size_t getSizeOfType(Type* type); + size_t getAlignmentOfType(Type* type); + + bool areTypesCovariant(Type* base, Type* derv); + bool areTypesContravariant(Type* base, Type* derv, bool traitChecking); + bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn, bool traitChecking); + bool areTypeListsContravariant(const std::vector& base, const std::vector& derv, bool traitChecking); + + enum class TypeKind { Invalid, @@ -81,10 +91,6 @@ namespace fir PolyPlaceholder, }; - // in theory. - size_t getSizeOfType(Type* type); - size_t getAlignmentOfType(Type* type); - struct Type { // stuff @@ -688,7 +694,9 @@ namespace fir void setBody(const std::vector>& members); - + void addTraitImpl(TraitType* trt); + bool implementsTrait(TraitType* trt); + std::vector getImplementedTraits(); virtual std::string str() override; virtual std::string encodedStr() override; @@ -704,6 +712,7 @@ namespace fir bool isTypePacked; Identifier structName; std::vector typeList; + std::vector implTraits; util::hash_map indexMap; util::hash_map structMembers; @@ -763,6 +772,10 @@ namespace fir Function* getCopyConstructor(); Function* getMoveConstructor(); + void addTraitImpl(TraitType* trt); + bool implementsTrait(TraitType* trt); + std::vector getImplementedTraits(); + bool hasParent(Type* base); void addVirtualMethod(Function* method); @@ -789,6 +802,8 @@ namespace fir std::vector methodList; std::vector initialiserList; + std::vector implTraits; + util::hash_map indexMap; util::hash_map classMembers; util::hash_map> classMethodMap; @@ -819,9 +834,6 @@ namespace fir static ClassType* createWithoutBody(const Identifier& name); static ClassType* create(const Identifier& name, const std::vector>& members, const std::vector& methods, const std::vector& inits); - - // returns true if 'fn' is a valid virtual override of 'base'. deals with co/contra-variance - static bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn); }; diff --git a/source/include/utils.h b/source/include/utils.h index d46d9fb6..179c92c4 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -209,6 +209,12 @@ namespace util return -1; } + template + bool contains(const std::vector& input, const T& x) + { + return std::find(input.begin(), input.end(), x) != input.end(); + } + template std::vector take(const std::vector& v, size_t num) { diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index f9324ba9..f6aa60dc 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -183,20 +183,15 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co auto checkSingleMethod = [](sst::ClassDefn* cls, sst::FunctionDefn* self, sst::FunctionDefn* bf, bool* matchedName) -> bool { - // ok -- issue is that we cannot compare the method signatures directly -- because the method will take the 'self' of its - // respective class, meaning they won't be duplicates. so, we must compare without the first parameter. - auto compareMethodSignatures = [](fir::FunctionType* a, fir::FunctionType* b) -> bool { - - // well the order is important!! - return fir::ClassType::areMethodsVirtuallyCompatible(a, b); - }; - if(bf->id.name == self->id.name) { *matchedName |= true; - if(!compareMethodSignatures(bf->type->toFunctionType(), self->type->toFunctionType())) + if(!fir::areMethodsVirtuallyCompatible(bf->type->toFunctionType(), self->type->toFunctionType(), + /* trait checking: */ false)) + { return false; + } // check for virtual functions. //* note: we don't need to care if 'bf' is the base method, because if we are 'isOverride', then we are also diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index d2fc73be..284dd6d4 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -191,6 +191,7 @@ TCResult ast::StructDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, c iceAssert(tdef); defn->traits.push_back(tdef); + str->addTraitImpl(tdef->type->toTraitType()); } From 07ffa9576c3d04132f8e91dbf266c948a3f2c826 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 25 Oct 2019 11:10:20 +0800 Subject: [PATCH 048/129] appveyor dun like my debuglogln --- source/fir/Types/TypeUtils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/fir/Types/TypeUtils.cpp b/source/fir/Types/TypeUtils.cpp index a1abf221..4faf1d32 100644 --- a/source/fir/Types/TypeUtils.cpp +++ b/source/fir/Types/TypeUtils.cpp @@ -90,7 +90,6 @@ namespace fir bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn, bool traitChecking) { - debuglogln("check %s, %s", base, fn); bool ret = areTypeListsContravariant(util::drop(base->getArgumentTypes(), 1), util::drop(fn->getArgumentTypes(), 1), traitChecking); From 1acd613ec0e65c55f3f9b9154b5fb8f80008cf1e Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 4 Nov 2019 22:44:32 +0800 Subject: [PATCH 049/129] stuff --- build/ultratiny.flx | 40 ++++++++++++++++++++++++++++++------ source/typecheck/classes.cpp | 33 ++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 383473c7..86000216 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -13,26 +13,39 @@ import std::io as _ @compiler_support["raii_trait::copy"] trait Copy { fn copy(other: &self) } @compiler_support["raii_trait::move"] trait Move { fn move(other: &mut self) } -struct Foo : Drop, Copy, Move +class Foo1 { - data: int + init() { } + deinit { printf("killed base\n"); } +} + +class Foo : Foo1, Drop, Copy, Move +{ + var data: int + + init(x: int) + { + printf("of make (%d)\n", x) + this.data = x + } fn deinit() { - printf("is kill\n") + printf("is kill (%d)\n", data) } fn copy(other: &self) { - printf("am copy\n") this.data = other.data + printf("am copy (%d)\n", data) } fn move(other: &mut self) { - printf("be move\n") this.data = other.data other.data = 0 + + printf("be move (%d)\n", data) } } @@ -47,7 +60,7 @@ class Bar fn one() -> Foo { - return Foo(data: 33) + return Foo(x: 33) } fn two(x: Foo) @@ -60,6 +73,8 @@ fn two(x: Foo) @entry fn main() { let q = one() + q.data = 17 + two(q) printf("q = %d\n", q) @@ -69,6 +84,19 @@ fn two(x: Foo) /* raii traits checklist: + problem with deinit: in any given class implementation, the user-defined deinit *needs* to call both: + (a) the inline destructor for the class + (b) the user-defined destructor for the base class + (c) the inline destructor for the base class + + so this is a bunch of work that isn't done? also, in the base class case, the derived class cannot properly + define a deinit() function, because of redefinitions. what's the solution? + + (1) everything becomes virtual (ew) + (2) method hiding (also ew) + (3) ??? + + 3. remove the copy/move/destruct stuff from FIR? */ diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index f6aa60dc..78a6a406 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -53,21 +53,34 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* // why do we do this when generating the declaration instead of only when we typecheck? // as it currently stands, this means that our base class + any traits must appear before // this class definition in the source code, which is kinda dumb. - if(this->bases.size() > 0) + for(auto base : util::map(this->bases, [fs](auto t) -> auto { return fs->convertParserTypeToFIR(t); })) { - auto base = fs->convertParserTypeToFIR(this->bases[0]); - if(!base->isClassType()) - error(this, "class '%s' can only inherit from a class, which '%s' is not", this->name, base); + if(base->isClassType()) + { + if(defn->baseClass) + error(this, "cannot inherit from more than one class (already inherited from '%s')", defn->baseClass->id.name); - cls->setBaseClass(base->toClassType()); + else if(!defn->traits.empty()) + error(this, "base class must come before any traits in the inheritance list"); - if(this->bases.size() > 1) - error(this, "cannot inherit from more than one class"); + auto basedef = dcast(sst::ClassDefn, fs->typeDefnMap[base]); + iceAssert(basedef); - auto basedef = dcast(sst::ClassDefn, fs->typeDefnMap[base]); - iceAssert(basedef); + defn->baseClass = basedef; + cls->setBaseClass(base->toClassType()); + } + else if(base->isTraitType()) + { + auto tdef = dcast(sst::TraitDefn, fs->typeDefnMap[base]); + iceAssert(tdef); - defn->baseClass = basedef; + defn->traits.push_back(tdef); + cls->addTraitImpl(tdef->type->toTraitType()); + } + else + { + error(this, "invalid type '%s' in inheritance list of class", base); + } } fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); From 878d1ad0bb9a727a6c85f6a9646c7b489f0a1ef7 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 4 Nov 2019 23:26:24 +0800 Subject: [PATCH 050/129] clean up options --- source/frontend/arguments.cpp | 291 +++++++++++++++++++--------------- source/include/frontend.h | 2 + source/main.cpp | 6 +- 3 files changed, 171 insertions(+), 128 deletions(-) diff --git a/source/frontend/arguments.cpp b/source/frontend/arguments.cpp index d58588ce..93dd016c 100644 --- a/source/frontend/arguments.cpp +++ b/source/frontend/arguments.cpp @@ -6,8 +6,6 @@ #include "frontend.h" #include "backend.h" -#include - #define FLAX_VERSION_STRING "0.41.7-pre" #define ARG_COMPILE_ONLY "-c" @@ -15,13 +13,12 @@ #define ARG_EMIT_LLVM_IR "-emit-llvm" #define ARG_LINK_FRAMEWORK "-framework" #define ARG_FRAMEWORK_SEARCH_PATH "-F" -#define ARG_HELP "-help" -#define ARG_VERSION "-version" +#define ARG_HELP "--help" +#define ARG_VERSION "--version" #define ARG_JITPROGRAM "-jit" #define ARG_LINK_LIBRARY "-l" #define ARG_LIBRARY_SEARCH_PATH "-L" #define ARG_MCMODEL "-mcmodel" -#define ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS "-no-auto-gconstr" #define ARG_OUTPUT_FILE "-o" #define ARG_OPTIMISATION_LEVEL_SELECT "-O" #define ARG_POSINDEPENDENT "-pic" @@ -29,7 +26,6 @@ #define ARG_PRINT_LLVMIR "-print-lir" #define ARG_PROFILE "-profile" #define ARG_RUNPROGRAM "-run" -#define ARG_SHOW_CLANG_OUTPUT "-show-clang" #define ARG_SYSROOT "-sysroot" #define ARG_TARGET "-target" #define ARG_FFI_ESCAPE "--ffi-escape" @@ -37,6 +33,7 @@ #define ARG_NOSTDLIB "--nostdlib" #define ARG_NO_RUNTIME_CHECKS "--no-runtime-checks" #define ARG_NO_RUNTIME_ERROR_STRINGS "--no-runtime-error-strings" +#define ARG_REPL "-repl" // for internal use! #define ARG_ABORT_ON_ERROR "-abort-on-error" @@ -53,52 +50,50 @@ #define WARNING_DISABLE_VARIABLE_CHECKER "-Wno-var-checker" -static std::vector> list; +static std::vector> helpList; static void setupMap() { - list.push_back({ ARG_COMPILE_ONLY, "output an object file; do not call the linker" }); - list.push_back({ ARG_BACKEND + std::string(" "), "change the backend used for compilation" }); - list.push_back({ ARG_EMIT_LLVM_IR, "emit a bitcode (.bc) file instead of a program" }); - list.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "link to a framework (macOS only)" }); - list.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "link to a framework (macOS only)" }); - list.push_back({ ARG_HELP, "print this message" }); - list.push_back({ ARG_JITPROGRAM, "use LLVM JIT to run the program, instead of compiling to a file" }); - list.push_back({ ARG_LINK_LIBRARY + std::string(" "), "link to a library" }); - list.push_back({ ARG_LIBRARY_SEARCH_PATH + std::string(" "), "search for libraries in " }); - list.push_back({ ARG_MCMODEL + std::string(" "), "change the mcmodel of the code" }); - list.push_back({ ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS, "disable calling constructors before main()" }); - list.push_back({ ARG_OUTPUT_FILE + std::string(" "), "set the name of the output file" }); - list.push_back({ ARG_OPTIMISATION_LEVEL_SELECT + std::string(""), "change the optimisation level; -O0, -O1, -O2, " - "-O3, and -Ox are valid options" }); - - list.push_back({ ARG_FREESTANDING, "generate a freestanding executable or object file" }); - list.push_back({ ARG_NOSTDLIB, "do not link with default libraries (libc/libm/msvcrt)" }); - list.push_back({ ARG_FFI_ESCAPE, "allow calling external functions (eg. libc) at compile-time" }); - - list.push_back({ ARG_POSINDEPENDENT, "generate position independent code" }); - list.push_back({ ARG_PRINT_FIR, "print the FlaxIR before compilation" }); - list.push_back({ ARG_PRINT_LLVMIR, "print the LLVM IR before compilation" }); - list.push_back({ ARG_PROFILE, "print internal compiler profiling statistics" }); - list.push_back({ ARG_RUNPROGRAM, "use LLVM JIT to run the program, instead of compiling to a file" }); - list.push_back({ ARG_SHOW_CLANG_OUTPUT, "show the output of calling the final compiler" }); - list.push_back({ ARG_SYSROOT + std::string(" "), "set the directory used as the sysroot" }); - list.push_back({ ARG_TARGET + std::string(" "), "change the compilation target" }); - - list.push_back({ WARNING_DISABLE_ALL, "disable all warnings" }); - list.push_back({ WARNINGS_AS_ERRORS, "treat all warnings as errors" }); - - list.push_back({ ARG_NO_RUNTIME_CHECKS, "disable all runtime checks" }); - list.push_back({ ARG_NO_RUNTIME_ERROR_STRINGS, "disable runtime error messages (program will just abort)" }); - - list.push_back({ WARNING_ENABLE_UNUSED_VARIABLE, "enable warnings for unused variables" }); - list.push_back({ WARNING_ENABLE_VARIABLE_CHECKER, "enable warnings from the variable state checker" }); - list.push_back({ WARNING_DISABLE_UNUSED_VARIABLE, "disable warnings for unused variables" }); - list.push_back({ WARNING_DISABLE_VARIABLE_CHECKER, "disable warnings from the variable state checker" }); + helpList.push_back({ ARG_COMPILE_ONLY, "output an object file; do not call the linker" }); + helpList.push_back({ ARG_BACKEND + std::string(" "), "change the backend used for compilation" }); + helpList.push_back({ ARG_EMIT_LLVM_IR, "emit a bitcode (.bc) file instead of a program" }); + helpList.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "link to a framework (macOS only)" }); + helpList.push_back({ ARG_LINK_FRAMEWORK + std::string(" "), "link to a framework (macOS only)" }); + helpList.push_back({ ARG_HELP, "print this message" }); + helpList.push_back({ ARG_JITPROGRAM, "use LLVM JIT to run the program, instead of compiling to a file" }); + helpList.push_back({ ARG_LINK_LIBRARY + std::string(" "), "link to a library" }); + helpList.push_back({ ARG_LIBRARY_SEARCH_PATH + std::string(" "), "search for libraries in " }); + helpList.push_back({ ARG_MCMODEL + std::string(" "), "change the mcmodel of the code" }); + helpList.push_back({ ARG_OUTPUT_FILE + std::string(" "), "set the name of the output file" }); + helpList.push_back({ ARG_OPTIMISATION_LEVEL_SELECT + std::string(""), "change the optimisation level; (-O[0-3], -Ox)" }); + + helpList.push_back({ ARG_FREESTANDING, "generate a freestanding executable or object file" }); + helpList.push_back({ ARG_NOSTDLIB, "do not link with default libraries (libc/libm/msvcrt)" }); + helpList.push_back({ ARG_FFI_ESCAPE, "allow calling external functions (eg. libc) at compile-time" }); + + helpList.push_back({ ARG_POSINDEPENDENT, "generate position independent code" }); + helpList.push_back({ ARG_PRINT_FIR, "print the FlaxIR before compilation" }); + helpList.push_back({ ARG_PRINT_LLVMIR, "print the LLVM IR before compilation" }); + helpList.push_back({ ARG_PROFILE, "print internal compiler profiling statistics" }); + helpList.push_back({ ARG_RUNPROGRAM, "run the program directly, instead of compiling to a file; defaults to the llvm backend" }); + helpList.push_back({ ARG_SYSROOT + std::string(" "), "set the directory used as the sysroot" }); + helpList.push_back({ ARG_TARGET + std::string(" "), "change the compilation target" }); + helpList.push_back({ ARG_REPL, "start in repl mode" }); + + helpList.push_back({ WARNING_DISABLE_ALL, "disable all warnings" }); + helpList.push_back({ WARNINGS_AS_ERRORS, "treat all warnings as errors" }); + + helpList.push_back({ ARG_NO_RUNTIME_CHECKS, "disable all runtime checks" }); + helpList.push_back({ ARG_NO_RUNTIME_ERROR_STRINGS, "disable runtime error messages (program will just abort)" }); + + helpList.push_back({ WARNING_ENABLE_UNUSED_VARIABLE, "enable warnings for unused variables" }); + helpList.push_back({ WARNING_ENABLE_VARIABLE_CHECKER, "enable warnings from the variable state checker" }); + helpList.push_back({ WARNING_DISABLE_UNUSED_VARIABLE, "disable warnings for unused variables" }); + helpList.push_back({ WARNING_DISABLE_VARIABLE_CHECKER, "disable warnings from the variable state checker" }); } static void printHelp() { - if(list.empty()) + if(helpList.empty()) setupMap(); printf("Flax Compiler - Version %s\n\n", FLAX_VERSION_STRING); @@ -107,7 +102,7 @@ static void printHelp() printf("options:\n"); size_t maxl = 0; - for(const auto& p : list) + for(const auto& p : helpList) { if(p.first.length() > maxl) maxl = p.first.length(); @@ -116,13 +111,15 @@ static void printHelp() maxl += 4; // ok - for(const auto& [ opt, desc ] : list) + for(const auto& [ opt, desc ] : helpList) printf(" %s%s%s\n", opt.c_str(), std::string(maxl - opt.length(), ' ').c_str(), desc.c_str()); printf("\n"); } + + static std::string parseQuotedString(char** argv, int& i) { std::string ret; @@ -149,47 +146,40 @@ static std::string parseQuotedString(char** argv, int& i) +namespace frontend +{ + static std::vector frameworksToLink; + static std::vector frameworkSearchPaths; + static std::vector librariesToLink; + static std::vector librarySearchPaths; + static bool _isPIC = false; + static bool _isRepl = false; + static bool _printFIR = false; + static bool _ffiEscape = false; + static bool _doProfiler = false; + static bool _printLLVMIR = false; + static bool _abortOnError = false; + static bool _isFreestanding = false; + static bool _noStandardLibraries = false; + static bool _noRuntimeChecks = false; + static bool _noRuntimeErrorStrings = false; + static std::string _mcModel; + static std::string _targetArch; + static std::string _sysrootPath; + static const std::string _prefixPath = "/usr/local/"; + using BackendOption = backend::BackendOption; + using ProgOutputMode = backend::ProgOutputMode; + using OptimisationLevel = backend::OptimisationLevel; -namespace frontend -{ - std::vector frameworksToLink; - std::vector frameworkSearchPaths; - - std::vector librariesToLink; - std::vector librarySearchPaths; - - bool _isPIC = false; - bool _printFIR = false; - bool _doProfiler = false; - bool _printLLVMIR = false; - bool _isFreestanding = false; - bool _printClangOutput = false; - bool _noStandardLibraries = false; - bool _noAutoGlobalConstructor = false; - bool _abortOnError = false; - bool _ffiEscape = false; - - bool _noRuntimeChecks = false; - bool _noRuntimeErrorStrings = false; - - std::string _mcModel; - std::string _targetArch; - std::string _sysrootPath; - const std::string _prefixPath = "/usr/local/"; - - using ProgOutputMode = backend::ProgOutputMode; - using BackendOption = backend::BackendOption; - using OptimisationLevel = backend::OptimisationLevel; - - auto _outputMode = ProgOutputMode::Program; - auto _backendCodegen = BackendOption::LLVM; - auto _optLevel = OptimisationLevel::Normal; + static auto _optLevel = OptimisationLevel::Normal; + static auto _outputMode = ProgOutputMode::Program; + static auto _backendCodegen = BackendOption::LLVM; OptimisationLevel getOptLevel() { @@ -231,6 +221,11 @@ namespace frontend return _ffiEscape; } + bool getIsReplMode() + { + return _isRepl; + } + bool getPrintFIR() { return _printFIR; @@ -241,7 +236,6 @@ namespace frontend return _printLLVMIR; } - std::vector getFrameworksToLink() { return frameworksToLink; @@ -298,8 +292,77 @@ namespace frontend } + + // nothing to do with synchronisation!! + // TODO: this is a bit dumb given our current boolean flags, but... meh + static std::unordered_set setOptions; + static util::hash_map> mutualExclusions; + static void setupFlagMutexes() + { + // repl is basically incompatible with everything, so list it first. + mutualExclusions[ARG_REPL].insert(ARG_RUNPROGRAM); + mutualExclusions[ARG_REPL].insert(ARG_TARGET); + mutualExclusions[ARG_REPL].insert(ARG_BACKEND); + mutualExclusions[ARG_REPL].insert(ARG_MCMODEL); + mutualExclusions[ARG_REPL].insert(ARG_JITPROGRAM); + mutualExclusions[ARG_REPL].insert(ARG_OUTPUT_FILE); + mutualExclusions[ARG_REPL].insert(ARG_COMPILE_ONLY); + mutualExclusions[ARG_REPL].insert(ARG_FREESTANDING); + mutualExclusions[ARG_REPL].insert(ARG_POSINDEPENDENT); + mutualExclusions[ARG_REPL].insert(ARG_OPTIMISATION_LEVEL_SELECT); + + // don't try to run/jit and compile/output at the same time + mutualExclusions[ARG_RUNPROGRAM].insert(ARG_TARGET); + mutualExclusions[ARG_RUNPROGRAM].insert(ARG_MCMODEL); + mutualExclusions[ARG_RUNPROGRAM].insert(ARG_OUTPUT_FILE); + mutualExclusions[ARG_RUNPROGRAM].insert(ARG_COMPILE_ONLY); + mutualExclusions[ARG_RUNPROGRAM].insert(ARG_FREESTANDING); + mutualExclusions[ARG_RUNPROGRAM].insert(ARG_POSINDEPENDENT); + + // just copy it. + mutualExclusions[ARG_JITPROGRAM] = mutualExclusions[ARG_RUNPROGRAM]; + + // ok now the trick is, for each exclusion, add the reverse. + for(const auto& [ x, xs ] : mutualExclusions) + for(const auto& y : xs) + mutualExclusions[y].insert(x); + } + + static void checkOptionExclusivity(const std::string& a) + { + for(const auto& ex : mutualExclusions[a]) + { + if(setOptions.find(ex) != setOptions.end()) + _error_and_exit("error: options '%s' and '%s' are mutually exclusive", a, ex); + } + } + + + + + + + + std::pair parseCmdLineOpts(int argc, char** argv) { + setupFlagMutexes(); + + // quick thing: usually programs will not do anything if --help or --version is anywhere in the flags. + for(int i = 1; i < argc; i++) + { + if(!strcmp(argv[i], ARG_HELP)) + { + printHelp(); + exit(0); + } + else if(!strcmp(argv[i], ARG_VERSION)) + { + printf("Flax Compiler (flaxc), version %s\n", FLAX_VERSION_STRING); + exit(0); + } + } + // parse arguments std::vector filenames; std::string outname; @@ -309,17 +372,8 @@ namespace frontend // parse the command line opts for(int i = 1; i < argc; i++) { - if(!strcmp(argv[i], ARG_HELP)) - { - printHelp(); - exit(0); - } - else if(!strcmp(argv[i], ARG_VERSION)) - { - printf("Flax Compiler (flaxc), version %s\n", FLAX_VERSION_STRING); - exit(0); - } - else if(!strcmp(argv[i], ARG_LINK_FRAMEWORK)) + bool wasFilename = false; + if(!strcmp(argv[i], ARG_LINK_FRAMEWORK)) { if(i != argc - 1) { @@ -447,6 +501,10 @@ namespace frontend { frontend::_ffiEscape = true; } + else if(!strcmp(argv[i], ARG_REPL)) + { + frontend::_isRepl = true; + } else if(!strcmp(argv[i], ARG_BACKEND)) { if(i != argc - 1) @@ -543,14 +601,6 @@ namespace frontend { frontend::_outputMode = ProgOutputMode::ObjectFile; } - else - { - _error_and_exit("error: cannot use '-c' option simultaneously with either '-emit-llvm' or '-jit'\n"); - } - } - else if(!strcmp(argv[i], ARG_SHOW_CLANG_OUTPUT)) - { - frontend::_printClangOutput = true; } else if(!strcmp(argv[i], ARG_JITPROGRAM) || !strcmp(argv[i], ARG_RUNPROGRAM)) { @@ -558,14 +608,6 @@ namespace frontend { frontend::_outputMode = ProgOutputMode::RunJit; } - else - { - _error_and_exit("error: cannot use '-jit'/'-run' option simultaneously with either '-emit-llvm' or '-c'\n"); - } - } - else if(!strcmp(argv[i], ARG_DISABLE_AUTO_GLOBAL_CONSTRUCTORS)) - { - frontend::_noAutoGlobalConstructor = true; } else if(strstr(argv[i], ARG_OPTIMISATION_LEVEL_SELECT) == argv[i]) { @@ -629,33 +671,28 @@ namespace frontend } else { + wasFilename = true; filenames.push_back(argv[i]); - continue; + } + + + if(!wasFilename) + { + setOptions.insert(argv[i]); + checkOptionExclusivity(argv[i]); } } } - else - { - _error_and_exit("expected at least one argument\n"); - } - if(filenames.empty()) - { + + + if(filenames.empty() && !frontend::_isRepl) _error_and_exit("error: no input files\n"); - } if(filenames.size() > 1) - { _error_and_exit("only one input file is supported at the moment\n"); - } - - - // sanity check - // what are you even trying to do m8 - if(frontend::_outputMode == ProgOutputMode::RunJit && frontend::_isFreestanding) - _error_and_exit("cannot JIT program in freestanding mode\n"); - return { filenames[0], outname }; + return { filenames.empty() ? "" : filenames[0], outname }; } } diff --git a/source/include/frontend.h b/source/include/frontend.h index b088c2df..682c0a2f 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -53,6 +53,8 @@ namespace frontend bool getIsNoRuntimeChecks(); bool getIsNoRuntimeErrorStrings(); + bool getIsReplMode(); + std::vector getFrameworksToLink(); std::vector getFrameworkSearchPaths(); std::vector getLibrariesToLink(); diff --git a/source/main.cpp b/source/main.cpp index 21e897ce..0b5ecd1c 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -164,7 +164,11 @@ int main(int argc, char** argv) platform::compiler::performSelfDlOpen(); auto [ input_file, output_file ] = frontend::parseCmdLineOpts(argc, argv); - compile(input_file, output_file); + + if(frontend::getIsReplMode()) ; + else compile(input_file, output_file); + + return 0; } From fabd1ef10639638bf1e8901585e9d71dafa2c7fd Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 5 Nov 2019 00:05:22 +0800 Subject: [PATCH 051/129] basic prompt --- external/linenoise/LICENSE | 25 + external/linenoise/README.markdown | 229 +++++ external/linenoise/encodings/utf8.c | 685 ++++++++++++++ external/linenoise/encodings/utf8.h | 55 ++ external/linenoise/linenoise.c | 1328 +++++++++++++++++++++++++++ external/linenoise/linenoise.h | 82 ++ external/linenoise/makefile | 20 + makefile | 10 +- source/frontend/arguments.cpp | 5 + source/include/frontend.h | 1 + source/include/repl.h | 11 + source/main.cpp | 3 +- source/repl/driver.cpp | 67 ++ 13 files changed, 2518 insertions(+), 3 deletions(-) create mode 100644 external/linenoise/LICENSE create mode 100644 external/linenoise/README.markdown create mode 100755 external/linenoise/encodings/utf8.c create mode 100755 external/linenoise/encodings/utf8.h create mode 100644 external/linenoise/linenoise.c create mode 100644 external/linenoise/linenoise.h create mode 100644 external/linenoise/makefile create mode 100644 source/include/repl.h create mode 100644 source/repl/driver.cpp diff --git a/external/linenoise/LICENSE b/external/linenoise/LICENSE new file mode 100644 index 00000000..18e81486 --- /dev/null +++ b/external/linenoise/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2010-2014, Salvatore Sanfilippo +Copyright (c) 2010-2013, Pieter Noordhuis + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/linenoise/README.markdown b/external/linenoise/README.markdown new file mode 100644 index 00000000..9f583c7a --- /dev/null +++ b/external/linenoise/README.markdown @@ -0,0 +1,229 @@ +# Linenoise + +A minimal, zero-config, BSD licensed, readline replacement used in Redis, +MongoDB, and Android. + +* Single and multi line editing mode with the usual key bindings implemented. +* History handling. +* Completion. +* Hints (suggestions at the right of the prompt as you type). +* About 1,100 lines of BSD license source code. +* Only uses a subset of VT100 escapes (ANSI.SYS compatible). + +## Can a line editing library be 20k lines of code? + +Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing? + +So what usually happens is either: + + * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh). + * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance). + +The result is a pollution of binaries without line editing support. + +So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not. + +## Terminals, in 2010. + +Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no +VT220 specific sequences are used anymore. + +The library is currently about 1100 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software. + +## Tested with... + + * Linux text only console ($TERM = linux) + * Linux KDE terminal application ($TERM = xterm) + * Linux xterm ($TERM = xterm) + * Linux Buildroot ($TERM = vt100) + * Mac OS X iTerm ($TERM = xterm) + * Mac OS X default Terminal.app ($TERM = xterm) + * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) + * IBM AIX 6.1 + * FreeBSD xterm ($TERM = xterm) + * ANSI.SYS + * Emacs comint mode ($TERM = dumb) + +Please test it everywhere you can and report back! + +## Let's push this forward! + +Patches should be provided in the respect of Linenoise sensibility for small +easy to understand code. + +Send feedbacks to antirez at gmail + +# The API + +Linenoise is very easy to use, and reading the example shipped with the +library should get you up to speed ASAP. Here is a list of API calls +and how to use them. + + char *linenoise(const char *prompt); + +This is the main Linenoise call: it shows the user a prompt with line editing +and history capabilities. The prompt you specify is used as a prompt, that is, +it will be printed to the left of the cursor. The library returns a buffer +with the line composed by the user, or NULL on end of file or when there +is an out of memory condition. + +When a tty is detected (the user is actually typing into a terminal session) +the maximum editable line length is `LINENOISE_MAX_LINE`. When instead the +standard input is not a tty, which happens every time you redirect a file +to a program, or use it in an Unix pipeline, there are no limits to the +length of the line that can be returned. + +The returned line should be freed with the `free()` standard system call. +However sometimes it could happen that your program uses a different dynamic +allocation library, so you may also used `linenoiseFree` to make sure the +line is freed with the same allocator it was created. + +The canonical loop used by a program using Linenoise will be something like +this: + + while((line = linenoise("hello> ")) != NULL) { + printf("You wrote: %s\n", line); + linenoiseFree(line); /* Or just free(line) if you use libc malloc. */ + } + +## Single line VS multi line editing + +By default, Linenoise uses single line editing, that is, a single row on the +screen will be used, and as the user types more, the text will scroll towards +left to make room. This works if your program is one where the user is +unlikely to write a lot of text, otherwise multi line editing, where multiple +screens rows are used, can be a lot more comfortable. + +In order to enable multi line editing use the following API call: + + linenoiseSetMultiLine(1); + +You can disable it using `0` as argument. + +## History + +Linenoise supporst history, so that the user does not have to retype +again and again the same things, but can use the down and up arrows in order +to search and re-edit already inserted lines of text. + +The followings are the history API calls: + + int linenoiseHistoryAdd(const char *line); + int linenoiseHistorySetMaxLen(int len); + int linenoiseHistorySave(const char *filename); + int linenoiseHistoryLoad(const char *filename); + +Use `linenoiseHistoryAdd` every time you want to add a new element +to the top of the history (it will be the first the user will see when +using the up arrow). + +Note that for history to work, you have to set a length for the history +(which is zero by default, so history will be disabled if you don't set +a proper one). This is accomplished using the `linenoiseHistorySetMaxLen` +function. + +Linenoise has direct support for persisting the history into an history +file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do +just that. Both functions return -1 on error and 0 on success. + +## Completion + +Linenoise supports completion, which is the ability to complete the user +input when she or he presses the `` key. + +In order to use completion, you need to register a completion callback, which +is called every time the user presses ``. Your callback will return a +list of items that are completions for the current string. + +The following is an example of registering a completion callback: + + linenoiseSetCompletionCallback(completion); + +The completion must be a function returning `void` and getting as input +a `const char` pointer, which is the line the user has typed so far, and +a `linenoiseCompletions` object pointer, which is used as argument of +`linenoiseAddCompletion` in order to add completions inside the callback. +An example will make it more clear: + + void completion(const char *buf, linenoiseCompletions *lc) { + if (buf[0] == 'h') { + linenoiseAddCompletion(lc,"hello"); + linenoiseAddCompletion(lc,"hello there"); + } + } + +Basically in your completion callback, you inspect the input, and return +a list of items that are good completions by using `linenoiseAddCompletion`. + +If you want to test the completion feature, compile the example program +with `make`, run it, type `h` and press ``. + +## Hints + +Linenoise has a feature called *hints* which is very useful when you +use Linenoise in order to implement a REPL (Read Eval Print Loop) for +a program that accepts commands and arguments, but may also be useful in +other conditions. + +The feature shows, on the right of the cursor, as the user types, hints that +may be useful. The hints can be displayed using a different color compared +to the color the user is typing, and can also be bold. + +For example as the user starts to type `"git remote add"`, with hints it's +possible to show on the right of the prompt a string ` `. + +The feature works similarly to the history feature, using a callback. +To register the callback we use: + + linenoiseSetHintsCallback(hints); + +The callback itself is implemented like this: + + char *hints(const char *buf, int *color, int *bold) { + if (!strcasecmp(buf,"git remote add")) { + *color = 35; + *bold = 0; + return " "; + } + return NULL; + } + +The callback function returns the string that should be displayed or NULL +if no hint is available for the text the user currently typed. The returned +string will be trimmed as needed depending on the number of columns available +on the screen. + +It is possible to return a string allocated in dynamic way, by also registering +a function to deallocate the hint string once used: + + void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); + +The free hint callback will just receive the pointer and free the string +as needed (depending on how the hits callback allocated it). + +As you can see in the example above, a `color` (in xterm color terminal codes) +can be provided together with a `bold` attribute. If no color is set, the +current terminal foreground color is used. If no bold attribute is set, +non-bold text is printed. + +Color codes are: + + red = 31 + green = 32 + yellow = 33 + blue = 34 + magenta = 35 + cyan = 36 + white = 37; + +## Screen handling + +Sometimes you may want to clear the screen as a result of something the +user typed. You can do this by calling the following function: + + void linenoiseClearScreen(void); + +## Related projects + +* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language. +* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift. diff --git a/external/linenoise/encodings/utf8.c b/external/linenoise/encodings/utf8.c new file mode 100755 index 00000000..1b23f925 --- /dev/null +++ b/external/linenoise/encodings/utf8.c @@ -0,0 +1,685 @@ +/* encoding/utf8.c -- VERSION 1.0 + * + * Guerrilla line editing library against the idea that a line editing lib + * needs to be 20,000 lines of C code. + * + * You can find the latest source code at: + * + * http://github.com/antirez/linenoise + * + * Does a number of crazy assumptions that happen to be true in 99.9999% of + * the 2010 UNIX computers around. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010-2014, Salvatore Sanfilippo + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define UNUSED(x) (void)(x) + +/* ============================ UTF8 utilities ============================== */ + +static unsigned long wideCharTable[][2] = { + { 0xA1, 0xA1 }, + { 0xA4, 0xA4 }, + { 0xA7, 0xA8 }, + { 0xAA, 0xAA }, + { 0xAD, 0xAE }, + { 0xB0, 0xB4 }, + { 0xB6, 0xBA }, + { 0xBC, 0xBF }, + { 0xC6, 0xC6 }, + { 0xD0, 0xD0 }, + { 0xD7, 0xD8 }, + { 0xDE, 0xE1 }, + { 0xE6, 0xE6 }, + { 0xE8, 0xEA }, + { 0xEC, 0xED }, + { 0xF0, 0xF0 }, + { 0xF2, 0xF3 }, + { 0xF7, 0xFA }, + { 0xFC, 0xFC }, + { 0xFE, 0xFE }, + { 0x101, 0x101 }, + { 0x111, 0x111 }, + { 0x113, 0x113 }, + { 0x11B, 0x11B }, + { 0x126, 0x127 }, + { 0x12B, 0x12B }, + { 0x131, 0x133 }, + { 0x138, 0x138 }, + { 0x13F, 0x142 }, + { 0x144, 0x144 }, + { 0x148, 0x14B }, + { 0x14D, 0x14D }, + { 0x152, 0x153 }, + { 0x166, 0x167 }, + { 0x16B, 0x16B }, + { 0x1CE, 0x1CE }, + { 0x1D0, 0x1D0 }, + { 0x1D2, 0x1D2 }, + { 0x1D4, 0x1D4 }, + { 0x1D6, 0x1D6 }, + { 0x1D8, 0x1D8 }, + { 0x1DA, 0x1DA }, + { 0x1DC, 0x1DC }, + { 0x251, 0x251 }, + { 0x261, 0x261 }, + { 0x2C4, 0x2C4 }, + { 0x2C7, 0x2C7 }, + { 0x2C9, 0x2CB }, + { 0x2CD, 0x2CD }, + { 0x2D0, 0x2D0 }, + { 0x2D8, 0x2DB }, + { 0x2DD, 0x2DD }, + { 0x2DF, 0x2DF }, + { 0x300, 0x36F }, + { 0x391, 0x3A1 }, + { 0x3A3, 0x3A9 }, + { 0x3B1, 0x3C1 }, + { 0x3C3, 0x3C9 }, + { 0x401, 0x401 }, + { 0x410, 0x44F }, + { 0x451, 0x451 }, + { 0x1100, 0x115F }, + { 0x2010, 0x2010 }, + { 0x2013, 0x2016 }, + { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, + { 0x2020, 0x2022 }, + { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, + { 0x2032, 0x2033 }, + { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, + { 0x203E, 0x203E }, + { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, + { 0x2081, 0x2084 }, + { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, + { 0x2105, 0x2105 }, + { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, + { 0x2116, 0x2116 }, + { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, + { 0x212B, 0x212B }, + { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, + { 0x2160, 0x216B }, + { 0x2170, 0x2179 }, + { 0x2189, 0x2189 }, + { 0x2190, 0x2199 }, + { 0x21B8, 0x21B9 }, + { 0x21D2, 0x21D2 }, + { 0x21D4, 0x21D4 }, + { 0x21E7, 0x21E7 }, + { 0x2200, 0x2200 }, + { 0x2202, 0x2203 }, + { 0x2207, 0x2208 }, + { 0x220B, 0x220B }, + { 0x220F, 0x220F }, + { 0x2211, 0x2211 }, + { 0x2215, 0x2215 }, + { 0x221A, 0x221A }, + { 0x221D, 0x2220 }, + { 0x2223, 0x2223 }, + { 0x2225, 0x2225 }, + { 0x2227, 0x222C }, + { 0x222E, 0x222E }, + { 0x2234, 0x2237 }, + { 0x223C, 0x223D }, + { 0x2248, 0x2248 }, + { 0x224C, 0x224C }, + { 0x2252, 0x2252 }, + { 0x2260, 0x2261 }, + { 0x2264, 0x2267 }, + { 0x226A, 0x226B }, + { 0x226E, 0x226F }, + { 0x2282, 0x2283 }, + { 0x2286, 0x2287 }, + { 0x2295, 0x2295 }, + { 0x2299, 0x2299 }, + { 0x22A5, 0x22A5 }, + { 0x22BF, 0x22BF }, + { 0x2312, 0x2312 }, + { 0x231A, 0x231B }, + { 0x2329, 0x232A }, + { 0x23E9, 0x23EC }, + { 0x23F0, 0x23F0 }, + { 0x23F3, 0x23F3 }, + { 0x2460, 0x24E9 }, + { 0x24EB, 0x254B }, + { 0x2550, 0x2573 }, + { 0x2580, 0x258F }, + { 0x2592, 0x2595 }, + { 0x25A0, 0x25A1 }, + { 0x25A3, 0x25A9 }, + { 0x25B2, 0x25B3 }, + { 0x25B6, 0x25B7 }, + { 0x25BC, 0x25BD }, + { 0x25C0, 0x25C1 }, + { 0x25C6, 0x25C8 }, + { 0x25CB, 0x25CB }, + { 0x25CE, 0x25D1 }, + { 0x25E2, 0x25E5 }, + { 0x25EF, 0x25EF }, + { 0x25FD, 0x25FE }, + { 0x2605, 0x2606 }, + { 0x2609, 0x2609 }, + { 0x260E, 0x260F }, + { 0x2614, 0x2615 }, + { 0x261C, 0x261C }, + { 0x261E, 0x261E }, + { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, + { 0x2648, 0x2653 }, + { 0x2660, 0x2661 }, + { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, + { 0x266C, 0x266D }, + { 0x266F, 0x266F }, + { 0x267F, 0x267F }, + { 0x2693, 0x2693 }, + { 0x269E, 0x269F }, + { 0x26A1, 0x26A1 }, + { 0x26AA, 0x26AB }, + { 0x26BD, 0x26BF }, + { 0x26C4, 0x26E1 }, + { 0x26E3, 0x26E3 }, + { 0x26E8, 0x26FF }, + { 0x2705, 0x2705 }, + { 0x270A, 0x270B }, + { 0x2728, 0x2728 }, + { 0x273D, 0x273D }, + { 0x274C, 0x274C }, + { 0x274E, 0x274E }, + { 0x2753, 0x2755 }, + { 0x2757, 0x2757 }, + { 0x2776, 0x277F }, + { 0x2795, 0x2797 }, + { 0x27B0, 0x27B0 }, + { 0x27BF, 0x27BF }, + { 0x2B1B, 0x2B1C }, + { 0x2B50, 0x2B50 }, + { 0x2B55, 0x2B59 }, + { 0x2E80, 0x2E99 }, + { 0x2E9B, 0x2EF3 }, + { 0x2F00, 0x2FD5 }, + { 0x2FF0, 0x2FFB }, + { 0x3000, 0x303E }, + { 0x3041, 0x3096 }, + { 0x3099, 0x30FF }, + { 0x3105, 0x312F }, + { 0x3131, 0x318E }, + { 0x3190, 0x31BA }, + { 0x31C0, 0x31E3 }, + { 0x31F0, 0x321E }, + { 0x3220, 0x4DBF }, + { 0x4E00, 0xA48C }, + { 0xA490, 0xA4C6 }, + { 0xA960, 0xA97C }, + { 0xAC00, 0xD7A3 }, + { 0xE000, 0xFAFF }, + { 0xFE00, 0xFE19 }, + { 0xFE30, 0xFE52 }, + { 0xFE54, 0xFE66 }, + { 0xFE68, 0xFE6B }, + { 0xFF01, 0xFF60 }, + { 0xFFE0, 0xFFE6 }, + { 0xFFFD, 0xFFFD }, + { 0x16FE0, 0x16FE3 }, + { 0x17000, 0x187F7 }, + { 0x18800, 0x18AF2 }, + { 0x1B000, 0x1B11E }, + { 0x1B150, 0x1B152 }, + { 0x1B164, 0x1B167 }, + { 0x1B170, 0x1B2FB }, + { 0x1F004, 0x1F004 }, + { 0x1F0CF, 0x1F0CF }, + { 0x1F100, 0x1F10A }, + { 0x1F110, 0x1F12D }, + { 0x1F130, 0x1F169 }, + { 0x1F170, 0x1F1AC }, + { 0x1F200, 0x1F202 }, + { 0x1F210, 0x1F23B }, + { 0x1F240, 0x1F248 }, + { 0x1F250, 0x1F251 }, + { 0x1F260, 0x1F265 }, + { 0x1F300, 0x1F320 }, + { 0x1F32D, 0x1F335 }, + { 0x1F337, 0x1F37C }, + { 0x1F37E, 0x1F393 }, + { 0x1F3A0, 0x1F3CA }, + { 0x1F3CF, 0x1F3D3 }, + { 0x1F3E0, 0x1F3F0 }, + { 0x1F3F4, 0x1F3F4 }, + { 0x1F3F8, 0x1F43E }, + { 0x1F440, 0x1F440 }, + { 0x1F442, 0x1F4FC }, + { 0x1F4FF, 0x1F53D }, + { 0x1F54B, 0x1F54E }, + { 0x1F550, 0x1F567 }, + { 0x1F57A, 0x1F57A }, + { 0x1F595, 0x1F596 }, + { 0x1F5A4, 0x1F5A4 }, + { 0x1F5FB, 0x1F64F }, + { 0x1F680, 0x1F6C5 }, + { 0x1F6CC, 0x1F6CC }, + { 0x1F6D0, 0x1F6D2 }, + { 0x1F6D5, 0x1F6D5 }, + { 0x1F6EB, 0x1F6EC }, + { 0x1F6F4, 0x1F6FA }, + { 0x1F7E0, 0x1F7EB }, + { 0x1F90D, 0x1F971 }, + { 0x1F973, 0x1F976 }, + { 0x1F97A, 0x1F9A2 }, + { 0x1F9A5, 0x1F9AA }, + { 0x1F9AE, 0x1F9CA }, + { 0x1F9CD, 0x1F9FF }, + { 0x1FA70, 0x1FA73 }, + { 0x1FA78, 0x1FA7A }, + { 0x1FA80, 0x1FA82 }, + { 0x1FA90, 0x1FA95 }, + { 0x20000, 0x2FFFD }, + { 0x30000, 0x3FFFD }, + { 0xE0100, 0xE01EF }, + { 0xF0000, 0xFFFFD }, + { 0x100000, 0x10FFFD }, +}; + +static size_t wideCharTableSize = sizeof(wideCharTable) / sizeof(wideCharTable[0]); + +static unsigned long combiningCharTable[] = { + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, + 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0591, 0x0592, 0x0593, + 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B, + 0x059C, 0x059D, 0x059E, 0x059F, 0x05A0, 0x05A1, 0x05A2, 0x05A3, + 0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB, + 0x05AC, 0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3, + 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB, + 0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7, + 0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, + 0x0618, 0x0619, 0x061A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, + 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, + 0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F, + 0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC, + 0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7, 0x06E8, + 0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0711, 0x0730, 0x0731, 0x0732, + 0x0733, 0x0734, 0x0735, 0x0736, 0x0737, 0x0738, 0x0739, 0x073A, + 0x073B, 0x073C, 0x073D, 0x073E, 0x073F, 0x0740, 0x0741, 0x0742, + 0x0743, 0x0744, 0x0745, 0x0746, 0x0747, 0x0748, 0x0749, 0x074A, + 0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD, + 0x07AE, 0x07AF, 0x07B0, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF, + 0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07FD, 0x0816, 0x0817, 0x0818, + 0x0819, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821, + 0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082A, 0x082B, + 0x082C, 0x082D, 0x0859, 0x085A, 0x085B, 0x08D3, 0x08D4, 0x08D5, + 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB, 0x08DC, 0x08DD, + 0x08DE, 0x08DF, 0x08E0, 0x08E1, 0x08E3, 0x08E4, 0x08E5, 0x08E6, + 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC, 0x08ED, 0x08EE, + 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4, 0x08F5, 0x08F6, + 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC, 0x08FD, 0x08FE, + 0x08FF, 0x0900, 0x0901, 0x0902, 0x093A, 0x093C, 0x0941, 0x0942, + 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, 0x0948, 0x094D, 0x0951, + 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, 0x0962, 0x0963, + 0x0981, 0x09BC, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09CD, 0x09E2, + 0x09E3, 0x09FE, 0x0A01, 0x0A02, 0x0A3C, 0x0A41, 0x0A42, 0x0A47, + 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A51, 0x0A70, 0x0A71, 0x0A75, + 0x0A81, 0x0A82, 0x0ABC, 0x0AC1, 0x0AC2, 0x0AC3, 0x0AC4, 0x0AC5, + 0x0AC7, 0x0AC8, 0x0ACD, 0x0AE2, 0x0AE3, 0x0AFA, 0x0AFB, 0x0AFC, + 0x0AFD, 0x0AFE, 0x0AFF, 0x0B01, 0x0B3C, 0x0B3F, 0x0B41, 0x0B42, + 0x0B43, 0x0B44, 0x0B4D, 0x0B56, 0x0B62, 0x0B63, 0x0B82, 0x0BC0, + 0x0BCD, 0x0C00, 0x0C04, 0x0C3E, 0x0C3F, 0x0C40, 0x0C46, 0x0C47, + 0x0C48, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D, 0x0C55, 0x0C56, 0x0C62, + 0x0C63, 0x0C81, 0x0CBC, 0x0CBF, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, + 0x0CE3, 0x0D00, 0x0D01, 0x0D3B, 0x0D3C, 0x0D41, 0x0D42, 0x0D43, + 0x0D44, 0x0D4D, 0x0D62, 0x0D63, 0x0DCA, 0x0DD2, 0x0DD3, 0x0DD4, + 0x0DD6, 0x0E31, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, + 0x0E3A, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, + 0x0E4E, 0x0EB1, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, 0x0EB8, 0x0EB9, + 0x0EBA, 0x0EBB, 0x0EBC, 0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, + 0x0ECD, 0x0F18, 0x0F19, 0x0F35, 0x0F37, 0x0F39, 0x0F71, 0x0F72, + 0x0F73, 0x0F74, 0x0F75, 0x0F76, 0x0F77, 0x0F78, 0x0F79, 0x0F7A, + 0x0F7B, 0x0F7C, 0x0F7D, 0x0F7E, 0x0F80, 0x0F81, 0x0F82, 0x0F83, + 0x0F84, 0x0F86, 0x0F87, 0x0F8D, 0x0F8E, 0x0F8F, 0x0F90, 0x0F91, + 0x0F92, 0x0F93, 0x0F94, 0x0F95, 0x0F96, 0x0F97, 0x0F99, 0x0F9A, + 0x0F9B, 0x0F9C, 0x0F9D, 0x0F9E, 0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, + 0x0FA3, 0x0FA4, 0x0FA5, 0x0FA6, 0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, + 0x0FAB, 0x0FAC, 0x0FAD, 0x0FAE, 0x0FAF, 0x0FB0, 0x0FB1, 0x0FB2, + 0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6, 0x0FB7, 0x0FB8, 0x0FB9, 0x0FBA, + 0x0FBB, 0x0FBC, 0x0FC6, 0x102D, 0x102E, 0x102F, 0x1030, 0x1032, + 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1039, 0x103A, 0x103D, + 0x103E, 0x1058, 0x1059, 0x105E, 0x105F, 0x1060, 0x1071, 0x1072, + 0x1073, 0x1074, 0x1082, 0x1085, 0x1086, 0x108D, 0x109D, 0x135D, + 0x135E, 0x135F, 0x1712, 0x1713, 0x1714, 0x1732, 0x1733, 0x1734, + 0x1752, 0x1753, 0x1772, 0x1773, 0x17B4, 0x17B5, 0x17B7, 0x17B8, + 0x17B9, 0x17BA, 0x17BB, 0x17BC, 0x17BD, 0x17C6, 0x17C9, 0x17CA, + 0x17CB, 0x17CC, 0x17CD, 0x17CE, 0x17CF, 0x17D0, 0x17D1, 0x17D2, + 0x17D3, 0x17DD, 0x180B, 0x180C, 0x180D, 0x1885, 0x1886, 0x18A9, + 0x1920, 0x1921, 0x1922, 0x1927, 0x1928, 0x1932, 0x1939, 0x193A, + 0x193B, 0x1A17, 0x1A18, 0x1A1B, 0x1A56, 0x1A58, 0x1A59, 0x1A5A, + 0x1A5B, 0x1A5C, 0x1A5D, 0x1A5E, 0x1A60, 0x1A62, 0x1A65, 0x1A66, + 0x1A67, 0x1A68, 0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A73, 0x1A74, + 0x1A75, 0x1A76, 0x1A77, 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, + 0x1A7F, 0x1AB0, 0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, + 0x1AB7, 0x1AB8, 0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1B00, + 0x1B01, 0x1B02, 0x1B03, 0x1B34, 0x1B36, 0x1B37, 0x1B38, 0x1B39, + 0x1B3A, 0x1B3C, 0x1B42, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, + 0x1B70, 0x1B71, 0x1B72, 0x1B73, 0x1B80, 0x1B81, 0x1BA2, 0x1BA3, + 0x1BA4, 0x1BA5, 0x1BA8, 0x1BA9, 0x1BAB, 0x1BAC, 0x1BAD, 0x1BE6, + 0x1BE8, 0x1BE9, 0x1BED, 0x1BEF, 0x1BF0, 0x1BF1, 0x1C2C, 0x1C2D, + 0x1C2E, 0x1C2F, 0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C36, 0x1C37, + 0x1CD0, 0x1CD1, 0x1CD2, 0x1CD4, 0x1CD5, 0x1CD6, 0x1CD7, 0x1CD8, + 0x1CD9, 0x1CDA, 0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, 0x1CE0, + 0x1CE2, 0x1CE3, 0x1CE4, 0x1CE5, 0x1CE6, 0x1CE7, 0x1CE8, 0x1CED, + 0x1CF4, 0x1CF8, 0x1CF9, 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, + 0x1DC5, 0x1DC6, 0x1DC7, 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, + 0x1DCD, 0x1DCE, 0x1DCF, 0x1DD0, 0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, + 0x1DD5, 0x1DD6, 0x1DD7, 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, + 0x1DDD, 0x1DDE, 0x1DDF, 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, + 0x1DE5, 0x1DE6, 0x1DE7, 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, + 0x1DED, 0x1DEE, 0x1DEF, 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, + 0x1DF5, 0x1DF6, 0x1DF7, 0x1DF8, 0x1DF9, 0x1DFB, 0x1DFC, 0x1DFD, + 0x1DFE, 0x1DFF, 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, + 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20E1, + 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, + 0x20ED, 0x20EE, 0x20EF, 0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2D7F, + 0x2DE0, 0x2DE1, 0x2DE2, 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, + 0x2DE8, 0x2DE9, 0x2DEA, 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, 0x2DEF, + 0x2DF0, 0x2DF1, 0x2DF2, 0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, + 0x2DF8, 0x2DF9, 0x2DFA, 0x2DFB, 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, + 0x302A, 0x302B, 0x302C, 0x302D, 0x3099, 0x309A, 0xA66F, 0xA674, + 0xA675, 0xA676, 0xA677, 0xA678, 0xA679, 0xA67A, 0xA67B, 0xA67C, + 0xA67D, 0xA69E, 0xA69F, 0xA6F0, 0xA6F1, 0xA802, 0xA806, 0xA80B, + 0xA825, 0xA826, 0xA8C4, 0xA8C5, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3, + 0xA8E4, 0xA8E5, 0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB, + 0xA8EC, 0xA8ED, 0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xA8FF, 0xA926, + 0xA927, 0xA928, 0xA929, 0xA92A, 0xA92B, 0xA92C, 0xA92D, 0xA947, + 0xA948, 0xA949, 0xA94A, 0xA94B, 0xA94C, 0xA94D, 0xA94E, 0xA94F, + 0xA950, 0xA951, 0xA980, 0xA981, 0xA982, 0xA9B3, 0xA9B6, 0xA9B7, + 0xA9B8, 0xA9B9, 0xA9BC, 0xA9BD, 0xA9E5, 0xAA29, 0xAA2A, 0xAA2B, + 0xAA2C, 0xAA2D, 0xAA2E, 0xAA31, 0xAA32, 0xAA35, 0xAA36, 0xAA43, + 0xAA4C, 0xAA7C, 0xAAB0, 0xAAB2, 0xAAB3, 0xAAB4, 0xAAB7, 0xAAB8, + 0xAABE, 0xAABF, 0xAAC1, 0xAAEC, 0xAAED, 0xAAF6, 0xABE5, 0xABE8, + 0xABED, 0xFB1E, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, + 0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, + 0xFE0E, 0xFE0F, 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, + 0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, + 0xFE2E, 0xFE2F, 0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379, + 0x1037A, 0x10A01, 0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0D, + 0x10A0E, 0x10A0F, 0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6, + 0x10D24, 0x10D25, 0x10D26, 0x10D27, 0x10F46, 0x10F47, 0x10F48, 0x10F49, + 0x10F4A, 0x10F4B, 0x10F4C, 0x10F4D, 0x10F4E, 0x10F4F, 0x10F50, 0x11001, + 0x11038, 0x11039, 0x1103A, 0x1103B, 0x1103C, 0x1103D, 0x1103E, 0x1103F, + 0x11040, 0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x1107F, + 0x11080, 0x11081, 0x110B3, 0x110B4, 0x110B5, 0x110B6, 0x110B9, 0x110BA, + 0x11100, 0x11101, 0x11102, 0x11127, 0x11128, 0x11129, 0x1112A, 0x1112B, + 0x1112D, 0x1112E, 0x1112F, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134, + 0x11173, 0x11180, 0x11181, 0x111B6, 0x111B7, 0x111B8, 0x111B9, 0x111BA, + 0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111C9, 0x111CA, 0x111CB, 0x111CC, + 0x1122F, 0x11230, 0x11231, 0x11234, 0x11236, 0x11237, 0x1123E, 0x112DF, + 0x112E3, 0x112E4, 0x112E5, 0x112E6, 0x112E7, 0x112E8, 0x112E9, 0x112EA, + 0x11300, 0x11301, 0x1133B, 0x1133C, 0x11340, 0x11366, 0x11367, 0x11368, + 0x11369, 0x1136A, 0x1136B, 0x1136C, 0x11370, 0x11371, 0x11372, 0x11373, + 0x11374, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D, 0x1143E, + 0x1143F, 0x11442, 0x11443, 0x11444, 0x11446, 0x1145E, 0x114B3, 0x114B4, + 0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114BA, 0x114BF, 0x114C0, 0x114C2, + 0x114C3, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115BC, 0x115BD, 0x115BF, + 0x115C0, 0x115DC, 0x115DD, 0x11633, 0x11634, 0x11635, 0x11636, 0x11637, + 0x11638, 0x11639, 0x1163A, 0x1163D, 0x1163F, 0x11640, 0x116AB, 0x116AD, + 0x116B0, 0x116B1, 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B7, 0x1171D, + 0x1171E, 0x1171F, 0x11722, 0x11723, 0x11724, 0x11725, 0x11727, 0x11728, + 0x11729, 0x1172A, 0x1172B, 0x1182F, 0x11830, 0x11831, 0x11832, 0x11833, + 0x11834, 0x11835, 0x11836, 0x11837, 0x11839, 0x1183A, 0x119D4, 0x119D5, + 0x119D6, 0x119D7, 0x119DA, 0x119DB, 0x119E0, 0x11A01, 0x11A02, 0x11A03, + 0x11A04, 0x11A05, 0x11A06, 0x11A07, 0x11A08, 0x11A09, 0x11A0A, 0x11A33, + 0x11A34, 0x11A35, 0x11A36, 0x11A37, 0x11A38, 0x11A3B, 0x11A3C, 0x11A3D, + 0x11A3E, 0x11A47, 0x11A51, 0x11A52, 0x11A53, 0x11A54, 0x11A55, 0x11A56, + 0x11A59, 0x11A5A, 0x11A5B, 0x11A8A, 0x11A8B, 0x11A8C, 0x11A8D, 0x11A8E, + 0x11A8F, 0x11A90, 0x11A91, 0x11A92, 0x11A93, 0x11A94, 0x11A95, 0x11A96, + 0x11A98, 0x11A99, 0x11C30, 0x11C31, 0x11C32, 0x11C33, 0x11C34, 0x11C35, + 0x11C36, 0x11C38, 0x11C39, 0x11C3A, 0x11C3B, 0x11C3C, 0x11C3D, 0x11C3F, + 0x11C92, 0x11C93, 0x11C94, 0x11C95, 0x11C96, 0x11C97, 0x11C98, 0x11C99, + 0x11C9A, 0x11C9B, 0x11C9C, 0x11C9D, 0x11C9E, 0x11C9F, 0x11CA0, 0x11CA1, + 0x11CA2, 0x11CA3, 0x11CA4, 0x11CA5, 0x11CA6, 0x11CA7, 0x11CAA, 0x11CAB, + 0x11CAC, 0x11CAD, 0x11CAE, 0x11CAF, 0x11CB0, 0x11CB2, 0x11CB3, 0x11CB5, + 0x11CB6, 0x11D31, 0x11D32, 0x11D33, 0x11D34, 0x11D35, 0x11D36, 0x11D3A, + 0x11D3C, 0x11D3D, 0x11D3F, 0x11D40, 0x11D41, 0x11D42, 0x11D43, 0x11D44, + 0x11D45, 0x11D47, 0x11D90, 0x11D91, 0x11D95, 0x11D97, 0x11EF3, 0x11EF4, + 0x16AF0, 0x16AF1, 0x16AF2, 0x16AF3, 0x16AF4, 0x16B30, 0x16B31, 0x16B32, + 0x16B33, 0x16B34, 0x16B35, 0x16B36, 0x16F4F, 0x16F8F, 0x16F90, 0x16F91, + 0x16F92, 0x1BC9D, 0x1BC9E, 0x1D167, 0x1D168, 0x1D169, 0x1D17B, 0x1D17C, + 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181, 0x1D182, 0x1D185, 0x1D186, + 0x1D187, 0x1D188, 0x1D189, 0x1D18A, 0x1D18B, 0x1D1AA, 0x1D1AB, 0x1D1AC, + 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00, 0x1DA01, 0x1DA02, 0x1DA03, + 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08, 0x1DA09, 0x1DA0A, 0x1DA0B, + 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10, 0x1DA11, 0x1DA12, 0x1DA13, + 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18, 0x1DA19, 0x1DA1A, 0x1DA1B, + 0x1DA1C, 0x1DA1D, 0x1DA1E, 0x1DA1F, 0x1DA20, 0x1DA21, 0x1DA22, 0x1DA23, + 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28, 0x1DA29, 0x1DA2A, 0x1DA2B, + 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30, 0x1DA31, 0x1DA32, 0x1DA33, + 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, 0x1DA3C, 0x1DA3D, 0x1DA3E, 0x1DA3F, + 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44, 0x1DA45, 0x1DA46, 0x1DA47, + 0x1DA48, 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C, 0x1DA4D, 0x1DA4E, 0x1DA4F, + 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54, 0x1DA55, 0x1DA56, 0x1DA57, + 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C, 0x1DA5D, 0x1DA5E, 0x1DA5F, + 0x1DA60, 0x1DA61, 0x1DA62, 0x1DA63, 0x1DA64, 0x1DA65, 0x1DA66, 0x1DA67, + 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C, 0x1DA75, 0x1DA84, 0x1DA9B, + 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1, 0x1DAA2, 0x1DAA3, 0x1DAA4, + 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9, 0x1DAAA, 0x1DAAB, 0x1DAAC, + 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001, 0x1E002, 0x1E003, 0x1E004, + 0x1E005, 0x1E006, 0x1E008, 0x1E009, 0x1E00A, 0x1E00B, 0x1E00C, 0x1E00D, + 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012, 0x1E013, 0x1E014, 0x1E015, + 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C, 0x1E01D, 0x1E01E, 0x1E01F, + 0x1E020, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E027, 0x1E028, 0x1E029, + 0x1E02A, 0x1E130, 0x1E131, 0x1E132, 0x1E133, 0x1E134, 0x1E135, 0x1E136, + 0x1E2EC, 0x1E2ED, 0x1E2EE, 0x1E2EF, 0x1E8D0, 0x1E8D1, 0x1E8D2, 0x1E8D3, + 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944, 0x1E945, 0x1E946, 0x1E947, 0x1E948, + 0x1E949, 0x1E94A, 0xE0100, 0xE0101, 0xE0102, 0xE0103, 0xE0104, 0xE0105, + 0xE0106, 0xE0107, 0xE0108, 0xE0109, 0xE010A, 0xE010B, 0xE010C, 0xE010D, + 0xE010E, 0xE010F, 0xE0110, 0xE0111, 0xE0112, 0xE0113, 0xE0114, 0xE0115, + 0xE0116, 0xE0117, 0xE0118, 0xE0119, 0xE011A, 0xE011B, 0xE011C, 0xE011D, + 0xE011E, 0xE011F, 0xE0120, 0xE0121, 0xE0122, 0xE0123, 0xE0124, 0xE0125, + 0xE0126, 0xE0127, 0xE0128, 0xE0129, 0xE012A, 0xE012B, 0xE012C, 0xE012D, + 0xE012E, 0xE012F, 0xE0130, 0xE0131, 0xE0132, 0xE0133, 0xE0134, 0xE0135, + 0xE0136, 0xE0137, 0xE0138, 0xE0139, 0xE013A, 0xE013B, 0xE013C, 0xE013D, + 0xE013E, 0xE013F, 0xE0140, 0xE0141, 0xE0142, 0xE0143, 0xE0144, 0xE0145, + 0xE0146, 0xE0147, 0xE0148, 0xE0149, 0xE014A, 0xE014B, 0xE014C, 0xE014D, + 0xE014E, 0xE014F, 0xE0150, 0xE0151, 0xE0152, 0xE0153, 0xE0154, 0xE0155, + 0xE0156, 0xE0157, 0xE0158, 0xE0159, 0xE015A, 0xE015B, 0xE015C, 0xE015D, + 0xE015E, 0xE015F, 0xE0160, 0xE0161, 0xE0162, 0xE0163, 0xE0164, 0xE0165, + 0xE0166, 0xE0167, 0xE0168, 0xE0169, 0xE016A, 0xE016B, 0xE016C, 0xE016D, + 0xE016E, 0xE016F, 0xE0170, 0xE0171, 0xE0172, 0xE0173, 0xE0174, 0xE0175, + 0xE0176, 0xE0177, 0xE0178, 0xE0179, 0xE017A, 0xE017B, 0xE017C, 0xE017D, + 0xE017E, 0xE017F, 0xE0180, 0xE0181, 0xE0182, 0xE0183, 0xE0184, 0xE0185, + 0xE0186, 0xE0187, 0xE0188, 0xE0189, 0xE018A, 0xE018B, 0xE018C, 0xE018D, + 0xE018E, 0xE018F, 0xE0190, 0xE0191, 0xE0192, 0xE0193, 0xE0194, 0xE0195, + 0xE0196, 0xE0197, 0xE0198, 0xE0199, 0xE019A, 0xE019B, 0xE019C, 0xE019D, + 0xE019E, 0xE019F, 0xE01A0, 0xE01A1, 0xE01A2, 0xE01A3, 0xE01A4, 0xE01A5, + 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9, 0xE01AA, 0xE01AB, 0xE01AC, 0xE01AD, + 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1, 0xE01B2, 0xE01B3, 0xE01B4, 0xE01B5, + 0xE01B6, 0xE01B7, 0xE01B8, 0xE01B9, 0xE01BA, 0xE01BB, 0xE01BC, 0xE01BD, + 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1, 0xE01C2, 0xE01C3, 0xE01C4, 0xE01C5, + 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9, 0xE01CA, 0xE01CB, 0xE01CC, 0xE01CD, + 0xE01CE, 0xE01CF, 0xE01D0, 0xE01D1, 0xE01D2, 0xE01D3, 0xE01D4, 0xE01D5, + 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9, 0xE01DA, 0xE01DB, 0xE01DC, 0xE01DD, + 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1, 0xE01E2, 0xE01E3, 0xE01E4, 0xE01E5, + 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9, 0xE01EA, 0xE01EB, 0xE01EC, 0xE01ED, + 0xE01EE, 0xE01EF, +}; + +static unsigned long combiningCharTableSize = sizeof(combiningCharTable) / sizeof(combiningCharTable[0]); + +/* Check if the code is a wide character + */ +static int isWideChar(unsigned long cp) { + size_t i; + for (i = 0; i < wideCharTableSize; i++) + if (wideCharTable[i][0] <= cp && cp <= wideCharTable[i][1]) return 1; + return 0; +} + +/* Check if the code is a combining character + */ +static int isCombiningChar(unsigned long cp) { + size_t i; + for (i = 0; i < combiningCharTableSize; i++) + if (combiningCharTable[i] == cp) return 1; + return 0; +} + +/* Get length of previous UTF8 character + */ +static size_t prevUtf8CharLen(const char* buf, int pos) { + int end = pos--; + while (pos >= 0 && ((unsigned char)buf[pos] & 0xC0) == 0x80) + pos--; + return end - pos; +} + +/* Convert UTF8 to Unicode code point + */ +static size_t utf8BytesToCodePoint(const char* buf, size_t len, int* cp) { + if (len) { + unsigned char byte = buf[0]; + if ((byte & 0x80) == 0) { + *cp = byte; + return 1; + } else if ((byte & 0xE0) == 0xC0) { + if (len >= 2) { + *cp = (((unsigned long)(buf[0] & 0x1F)) << 6) | + ((unsigned long)(buf[1] & 0x3F)); + return 2; + } + } else if ((byte & 0xF0) == 0xE0) { + if (len >= 3) { + *cp = (((unsigned long)(buf[0] & 0x0F)) << 12) | + (((unsigned long)(buf[1] & 0x3F)) << 6) | + ((unsigned long)(buf[2] & 0x3F)); + return 3; + } + } else if ((byte & 0xF8) == 0xF0) { + if (len >= 4) { + *cp = (((unsigned long)(buf[0] & 0x07)) << 18) | + (((unsigned long)(buf[1] & 0x3F)) << 12) | + (((unsigned long)(buf[2] & 0x3F)) << 6) | + ((unsigned long)(buf[3] & 0x3F)); + return 4; + } + } + } + return 0; +} + +/* Get length of next grapheme + */ +size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) { + size_t beg = pos; + int cp; + size_t len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp); + if (isCombiningChar(cp)) { + /* NOTREACHED */ + return 0; + } + if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1; + pos += len; + while (pos < buf_len) { + int cp; + len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp); + if (!isCombiningChar(cp)) return pos - beg; + pos += len; + } + return pos - beg; +} + +/* Get length of previous grapheme + */ +size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) { + UNUSED(buf_len); + size_t end = pos; + while (pos > 0) { + size_t len = prevUtf8CharLen(buf, pos); + pos -= len; + int cp; + utf8BytesToCodePoint(buf + pos, len, &cp); + if (!isCombiningChar(cp)) { + if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1; + return end - pos; + } + } + /* NOTREACHED */ + return 0; +} + +/* Read a Unicode from file. + */ +size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp) { + if (buf_len < 1) return -1; + size_t nread = read(fd,&buf[0],1); + if (nread <= 0) return nread; + + unsigned char byte = buf[0]; + if ((byte & 0x80) == 0) { + ; + } else if ((byte & 0xE0) == 0xC0) { + if (buf_len < 2) return -1; + nread = read(fd,&buf[1],1); + if (nread <= 0) return nread; + } else if ((byte & 0xF0) == 0xE0) { + if (buf_len < 3) return -1; + nread = read(fd,&buf[1],2); + if (nread <= 0) return nread; + } else if ((byte & 0xF8) == 0xF0) { + if (buf_len < 3) return -1; + nread = read(fd,&buf[1],3); + if (nread <= 0) return nread; + } else { + return -1; + } + + return utf8BytesToCodePoint(buf, buf_len, cp); +} diff --git a/external/linenoise/encodings/utf8.h b/external/linenoise/encodings/utf8.h new file mode 100755 index 00000000..d401bc86 --- /dev/null +++ b/external/linenoise/encodings/utf8.h @@ -0,0 +1,55 @@ +/* encodings/utf8.h -- VERSION 1.0 + * + * Guerrilla line editing library against the idea that a line editing lib + * needs to be 20,000 lines of C code. + * + * See linenoise.c for more information. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010-2014, Salvatore Sanfilippo + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LINENOISE_ENCODINGS_UTF8_H +#define __LINENOISE_ENCODINGS_UTF8_H + +#ifdef __cplusplus +extern "C" { +#endif + +size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len); +size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len); +size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp); + +#ifdef __cplusplus +} +#endif + +#endif /* __LINENOISE_ENCODINGS_UTF8_H */ + diff --git a/external/linenoise/linenoise.c b/external/linenoise/linenoise.c new file mode 100644 index 00000000..1550f197 --- /dev/null +++ b/external/linenoise/linenoise.c @@ -0,0 +1,1328 @@ +/* linenoise.c -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. + * + * You can find the latest source code at: + * + * http://github.com/antirez/linenoise + * + * Does a number of crazy assumptions that happen to be true in 99.9999% of + * the 2010 UNIX computers around. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010-2016, Salvatore Sanfilippo + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ------------------------------------------------------------------------ + * + * References: + * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html + * + * Todo list: + * - Filter bogus Ctrl+ combinations. + * - Win32 support + * + * Bloat: + * - History search like Ctrl+r in readline? + * + * List of escape sequences used by this program, we do everything just + * with three sequences. In order to be so cheap we may have some + * flickering effect with some slow terminal, but the lesser sequences + * the more compatible. + * + * EL (Erase Line) + * Sequence: ESC [ n K + * Effect: if n is 0 or missing, clear from cursor to end of line + * Effect: if n is 1, clear from beginning of line to cursor + * Effect: if n is 2, clear entire line + * + * CUF (CUrsor Forward) + * Sequence: ESC [ n C + * Effect: moves cursor forward n chars + * + * CUB (CUrsor Backward) + * Sequence: ESC [ n D + * Effect: moves cursor backward n chars + * + * The following is used to get the terminal width if getting + * the width with the TIOCGWINSZ ioctl fails + * + * DSR (Device Status Report) + * Sequence: ESC [ 6 n + * Effect: reports the current cusor position as ESC [ n ; m R + * where n is the row and m is the column + * + * When multi line mode is enabled, we also use an additional escape + * sequence. However multi line editing is disabled by default. + * + * CUU (Cursor Up) + * Sequence: ESC [ n A + * Effect: moves cursor up of n chars. + * + * CUD (Cursor Down) + * Sequence: ESC [ n B + * Effect: moves cursor down of n chars. + * + * When linenoiseClearScreen() is called, two additional escape sequences + * are used in order to clear the screen and position the cursor at home + * position. + * + * CUP (Cursor position) + * Sequence: ESC [ H + * Effect: moves the cursor to upper left corner + * + * ED (Erase display) + * Sequence: ESC [ 2 J + * Effect: clear the whole screen + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linenoise.h" + +#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 +#define LINENOISE_MAX_LINE 4096 +#define UNUSED(x) (void)(x) +static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; +static linenoiseCompletionCallback *completionCallback = NULL; +static linenoiseHintsCallback *hintsCallback = NULL; +static linenoiseFreeHintsCallback *freeHintsCallback = NULL; + +static struct termios orig_termios; /* In order to restore at exit.*/ +static int rawmode = 0; /* For atexit() function to check if restore is needed*/ +static int mlmode = 0; /* Multi line mode. Default is single line. */ +static int atexit_registered = 0; /* Register atexit just 1 time. */ +static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; +static int history_len = 0; +static char **history = NULL; + +/* The linenoiseState structure represents the state during line editing. + * We pass this state to functions implementing specific editing + * functionalities. */ +struct linenoiseState { + int ifd; /* Terminal stdin file descriptor. */ + int ofd; /* Terminal stdout file descriptor. */ + char *buf; /* Edited line buffer. */ + size_t buflen; /* Edited line buffer size. */ + const char *prompt; /* Prompt to display. */ + size_t plen; /* Prompt length. */ + size_t pos; /* Current cursor position. */ + size_t oldcolpos; /* Previous refresh cursor column position. */ + size_t len; /* Current edited line length. */ + size_t cols; /* Number of columns in terminal. */ + size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ + int history_index; /* The history index we are currently editing. */ +}; + +enum KEY_ACTION{ + KEY_NULL = 0, /* NULL */ + CTRL_A = 1, /* Ctrl+a */ + CTRL_B = 2, /* Ctrl-b */ + CTRL_C = 3, /* Ctrl-c */ + CTRL_D = 4, /* Ctrl-d */ + CTRL_E = 5, /* Ctrl-e */ + CTRL_F = 6, /* Ctrl-f */ + CTRL_H = 8, /* Ctrl-h */ + TAB = 9, /* Tab */ + CTRL_K = 11, /* Ctrl+k */ + CTRL_L = 12, /* Ctrl+l */ + ENTER = 13, /* Enter */ + CTRL_N = 14, /* Ctrl-n */ + CTRL_P = 16, /* Ctrl-p */ + CTRL_T = 20, /* Ctrl-t */ + CTRL_U = 21, /* Ctrl+u */ + CTRL_W = 23, /* Ctrl+w */ + ESC = 27, /* Escape */ + BACKSPACE = 127 /* Backspace */ +}; + +static void linenoiseAtExit(void); +int linenoiseHistoryAdd(const char *line); +static void refreshLine(struct linenoiseState *l); + +/* Debugging macro. */ +#if 0 +FILE *lndebug_fp = NULL; +#define lndebug(...) \ + do { \ + if (lndebug_fp == NULL) { \ + lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ + fprintf(lndebug_fp, \ + "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ + (int)l->len,(int)l->pos,(int)l->oldcolpos,plen,rows,rpos, \ + (int)l->maxrows,old_rows); \ + } \ + fprintf(lndebug_fp, ", " __VA_ARGS__); \ + fflush(lndebug_fp); \ + } while (0) +#else +#define lndebug(fmt, ...) +#endif + +/* ========================== Encoding functions ============================= */ + +/* Get byte length and column length of the previous character */ +static size_t defaultPrevCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) { + UNUSED(buf); UNUSED(buf_len); UNUSED(pos); + if (col_len != NULL) *col_len = 1; + return 1; +} + +/* Get byte length and column length of the next character */ +static size_t defaultNextCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) { + UNUSED(buf); UNUSED(buf_len); UNUSED(pos); + if (col_len != NULL) *col_len = 1; + return 1; +} + +/* Read bytes of the next character */ +static size_t defaultReadCode(int fd, char *buf, size_t buf_len, int* c) { + if (buf_len < 1) return -1; + int nread = read(fd,&buf[0],1); + if (nread == 1) *c = buf[0]; + return nread; +} + +/* Set default encoding functions */ +static linenoisePrevCharLen *prevCharLen = defaultPrevCharLen; +static linenoiseNextCharLen *nextCharLen = defaultNextCharLen; +static linenoiseReadCode *readCode = defaultReadCode; + +/* Set used defined encoding functions */ +void linenoiseSetEncodingFunctions( + linenoisePrevCharLen *prevCharLenFunc, + linenoiseNextCharLen *nextCharLenFunc, + linenoiseReadCode *readCodeFunc) { + prevCharLen = prevCharLenFunc; + nextCharLen = nextCharLenFunc; + readCode = readCodeFunc; +} + +/* Get column length from begining of buffer to current byte position */ +static size_t columnPos(const char *buf, size_t buf_len, size_t pos) { + size_t ret = 0; + size_t off = 0; + while (off < pos) { + size_t col_len; + size_t len = nextCharLen(buf,buf_len,off,&col_len); + off += len; + ret += col_len; + } + return ret; +} + +/* Get column length from begining of buffer to current byte position for multiline mode*/ +static size_t columnPosForMultiLine(const char *buf, size_t buf_len, size_t pos, size_t cols, size_t ini_pos) { + size_t ret = 0; + size_t colwid = ini_pos; + + size_t off = 0; + while (off < buf_len) { + size_t col_len; + size_t len = nextCharLen(buf,buf_len,off,&col_len); + + int dif = (int)(colwid + col_len) - (int)cols; + if (dif > 0) { + ret += dif; + colwid = col_len; + } else if (dif == 0) { + colwid = 0; + } else { + colwid += col_len; + } + + if (off >= pos) break; + off += len; + ret += col_len; + } + + return ret; +} + +/* ======================= Low level terminal handling ====================== */ + +/* Set if to use or not the multi line mode. */ +void linenoiseSetMultiLine(int ml) { + mlmode = ml; +} + +/* Return true if the terminal name is in the list of terminals we know are + * not able to understand basic escape sequences. */ +static int isUnsupportedTerm(void) { + char *term = getenv("TERM"); + int j; + + if (term == NULL) return 0; + for (j = 0; unsupported_term[j]; j++) + if (!strcasecmp(term,unsupported_term[j])) return 1; + return 0; +} + +/* Raw mode: 1960 magic shit. */ +static int enableRawMode(int fd) { + struct termios raw; + + if (!isatty(STDIN_FILENO)) goto fatal; + if (!atexit_registered) { + atexit(linenoiseAtExit); + atexit_registered = 1; + } + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; + rawmode = 1; + return 0; + +fatal: + errno = ENOTTY; + return -1; +} + +static void disableRawMode(int fd) { + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) + rawmode = 0; +} + +/* Use the ESC [6n escape sequence to query the horizontal cursor position + * and return it. On error -1 is returned, on success the position of the + * cursor. */ +static int getCursorPosition(int ifd, int ofd) { + char buf[32]; + int cols, rows; + unsigned int i = 0; + + /* Report cursor location */ + if (write(ofd, "\x1b[6n", 4) != 4) return -1; + + /* Read the response: ESC [ rows ; cols R */ + while (i < sizeof(buf)-1) { + if (read(ifd,buf+i,1) != 1) break; + if (buf[i] == 'R') break; + i++; + } + buf[i] = '\0'; + + /* Parse it. */ + if (buf[0] != ESC || buf[1] != '[') return -1; + if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; + return cols; +} + +/* Try to get the number of columns in the current terminal, or assume 80 + * if it fails. */ +static int getColumns(int ifd, int ofd) { + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { + /* ioctl() failed. Try to query the terminal itself. */ + int start, cols; + + /* Get the initial position so we can restore it later. */ + start = getCursorPosition(ifd,ofd); + if (start == -1) goto failed; + + /* Go to right margin and get position. */ + if (write(ofd,"\x1b[999C",6) != 6) goto failed; + cols = getCursorPosition(ifd,ofd); + if (cols == -1) goto failed; + + /* Restore position. */ + if (cols > start) { + char seq[32]; + snprintf(seq,32,"\x1b[%dD",cols-start); + if (write(ofd,seq,strlen(seq)) == -1) { + /* Can't recover... */ + } + } + return cols; + } else { + return ws.ws_col; + } + +failed: + return 80; +} + +/* Clear the screen. Used to handle ctrl+l */ +void linenoiseClearScreen(void) { + if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { + /* nothing to do, just to avoid warning. */ + } +} + +/* Beep, used for completion when there is nothing to complete or when all + * the choices were already shown. */ +static void linenoiseBeep(void) { + fprintf(stderr, "\x7"); + fflush(stderr); +} + +/* ============================== Completion ================================ */ + +/* Free a list of completion option populated by linenoiseAddCompletion(). */ +static void freeCompletions(linenoiseCompletions *lc) { + size_t i; + for (i = 0; i < lc->len; i++) + free(lc->cvec[i]); + if (lc->cvec != NULL) + free(lc->cvec); +} + +/* This is an helper function for linenoiseEdit() and is called when the + * user types the key in order to complete the string currently in the + * input. + * + * The state of the editing is encapsulated into the pointed linenoiseState + * structure as described in the structure definition. */ +static int completeLine(struct linenoiseState *ls, char *cbuf, size_t cbuf_len, int *c) { + linenoiseCompletions lc = { 0, NULL }; + int nread = 0, nwritten; + *c = 0; + + completionCallback(ls->buf,&lc); + if (lc.len == 0) { + linenoiseBeep(); + } else { + size_t stop = 0, i = 0; + + while(!stop) { + /* Show completion or original buffer */ + if (i < lc.len) { + struct linenoiseState saved = *ls; + + ls->len = ls->pos = strlen(lc.cvec[i]); + ls->buf = lc.cvec[i]; + refreshLine(ls); + ls->len = saved.len; + ls->pos = saved.pos; + ls->buf = saved.buf; + } else { + refreshLine(ls); + } + + nread = readCode(ls->ifd,cbuf,cbuf_len,c); + if (nread <= 0) { + freeCompletions(&lc); + *c = -1; + return nread; + } + + switch(*c) { + case 9: /* tab */ + i = (i+1) % (lc.len+1); + if (i == lc.len) linenoiseBeep(); + break; + case 27: /* escape */ + /* Re-show original buffer */ + if (i < lc.len) refreshLine(ls); + stop = 1; + break; + default: + /* Update buffer and return */ + if (i < lc.len) { + nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); + ls->len = ls->pos = nwritten; + } + stop = 1; + break; + } + } + } + + freeCompletions(&lc); + return nread; +} + +/* Register a callback function to be called for tab-completion. */ +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { + completionCallback = fn; +} + +/* Register a hits function to be called to show hits to the user at the + * right of the prompt. */ +void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { + hintsCallback = fn; +} + +/* Register a function to free the hints returned by the hints callback + * registered with linenoiseSetHintsCallback(). */ +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { + freeHintsCallback = fn; +} + +/* This function is used by the callback function registered by the user + * in order to add completion options given the input string when the + * user typed . See the example.c source code for a very easy to + * understand example. */ +void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { + size_t len = strlen(str); + char *copy, **cvec; + + copy = malloc(len+1); + if (copy == NULL) return; + memcpy(copy,str,len+1); + cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); + if (cvec == NULL) { + free(copy); + return; + } + lc->cvec = cvec; + lc->cvec[lc->len++] = copy; +} + +/* =========================== Line editing ================================= */ + +/* We define a very simple "append buffer" structure, that is an heap + * allocated string where we can append to. This is useful in order to + * write all the escape sequences in a buffer and flush them to the standard + * output in a single call, to avoid flickering effects. */ +struct abuf { + char *b; + int len; +}; + +static void abInit(struct abuf *ab) { + ab->b = NULL; + ab->len = 0; +} + +static void abAppend(struct abuf *ab, const char *s, int len) { + char *new = realloc(ab->b,ab->len+len); + + if (new == NULL) return; + memcpy(new+ab->len,s,len); + ab->b = new; + ab->len += len; +} + +static void abFree(struct abuf *ab) { + free(ab->b); +} + +/* Helper of refreshSingleLine() and refreshMultiLine() to show hints + * to the right of the prompt. */ +void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int pcollen) { + char seq[64]; + size_t collen = pcollen+columnPos(l->buf,l->len,l->len); + if (hintsCallback && collen < l->cols) { + int color = -1, bold = 0; + char *hint = hintsCallback(l->buf,&color,&bold); + if (hint) { + int hintlen = strlen(hint); + int hintmaxlen = l->cols-collen; + if (hintlen > hintmaxlen) hintlen = hintmaxlen; + if (bold == 1 && color == -1) color = 37; + if (color != -1 || bold != 0) + snprintf(seq,64,"\033[%d;%d;49m",bold,color); + else + seq[0] = '\0'; + abAppend(ab,seq,strlen(seq)); + abAppend(ab,hint,hintlen); + if (color != -1 || bold != 0) + abAppend(ab,"\033[0m",4); + /* Call the function to free the hint returned. */ + if (freeHintsCallback) freeHintsCallback(hint); + } + } +} + +/* Check if text is an ANSI escape sequence + */ +static int isAnsiEscape(const char *buf, size_t buf_len, size_t* len) { + if (buf_len > 2 && !memcmp("\033[", buf, 2)) { + size_t off = 2; + while (off < buf_len) { + switch (buf[off++]) { + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'J': case 'K': + case 'S': case 'T': case 'f': case 'm': + *len = off; + return 1; + } + } + } + return 0; +} + +/* Get column length of prompt text + */ +static size_t promptTextColumnLen(const char *prompt, size_t plen) { + char buf[LINENOISE_MAX_LINE]; + size_t buf_len = 0; + size_t off = 0; + while (off < plen) { + size_t len; + if (isAnsiEscape(prompt + off, plen - off, &len)) { + off += len; + continue; + } + buf[buf_len++] = prompt[off++]; + } + return columnPos(buf,buf_len,buf_len); +} + +/* Single line low level line refresh. + * + * Rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. */ +static void refreshSingleLine(struct linenoiseState *l) { + char seq[64]; + size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt)); + int fd = l->ofd; + char *buf = l->buf; + size_t len = l->len; + size_t pos = l->pos; + struct abuf ab; + + while((pcollen+columnPos(buf,len,pos)) >= l->cols) { + int chlen = nextCharLen(buf,len,0,NULL); + buf += chlen; + len -= chlen; + pos -= chlen; + } + while (pcollen+columnPos(buf,len,len) > l->cols) { + len -= prevCharLen(buf,len,len,NULL); + } + + abInit(&ab); + /* Cursor to left edge */ + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + /* Write the prompt and the current buffer content */ + abAppend(&ab,l->prompt,strlen(l->prompt)); + abAppend(&ab,buf,len); + /* Show hits if any. */ + refreshShowHints(&ab,l,pcollen); + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + abAppend(&ab,seq,strlen(seq)); + /* Move cursor to original position. */ + snprintf(seq,64,"\r\x1b[%dC", (int)(columnPos(buf,len,pos)+pcollen)); + abAppend(&ab,seq,strlen(seq)); + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); +} + +/* Multi line low level line refresh. + * + * Rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. */ +static void refreshMultiLine(struct linenoiseState *l) { + char seq[64]; + size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt)); + int colpos = columnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcollen); + int colpos2; /* cursor column position. */ + int rows = (pcollen+colpos+l->cols-1)/l->cols; /* rows used by current buf. */ + int rpos = (pcollen+l->oldcolpos+l->cols)/l->cols; /* cursor relative row. */ + int rpos2; /* rpos after refresh. */ + int col; /* colum position, zero-based. */ + int old_rows = l->maxrows; + int fd = l->ofd, j; + struct abuf ab; + + /* Update maxrows if needed. */ + if (rows > (int)l->maxrows) l->maxrows = rows; + + /* First step: clear all the lines used before. To do so start by + * going to the last row. */ + abInit(&ab); + if (old_rows-rpos > 0) { + lndebug("go down %d", old_rows-rpos); + snprintf(seq,64,"\x1b[%dB", old_rows-rpos); + abAppend(&ab,seq,strlen(seq)); + } + + /* Now for every row clear it, go up. */ + for (j = 0; j < old_rows-1; j++) { + lndebug("clear+up"); + snprintf(seq,64,"\r\x1b[0K\x1b[1A"); + abAppend(&ab,seq,strlen(seq)); + } + + /* Clean the top line. */ + lndebug("clear"); + snprintf(seq,64,"\r\x1b[0K"); + abAppend(&ab,seq,strlen(seq)); + + /* Write the prompt and the current buffer content */ + abAppend(&ab,l->prompt,strlen(l->prompt)); + abAppend(&ab,l->buf,l->len); + + /* Show hits if any. */ + refreshShowHints(&ab,l,pcollen); + + /* Get column length to cursor position */ + colpos2 = columnPosForMultiLine(l->buf,l->len,l->pos,l->cols,pcollen); + + /* If we are at the very end of the screen with our prompt, we need to + * emit a newline and move the prompt to the first column. */ + if (l->pos && + l->pos == l->len && + (colpos2+pcollen) % l->cols == 0) + { + lndebug(""); + abAppend(&ab,"\n",1); + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + rows++; + if (rows > (int)l->maxrows) l->maxrows = rows; + } + + /* Move cursor to right position. */ + rpos2 = (pcollen+colpos2+l->cols)/l->cols; /* current cursor relative row. */ + lndebug("rpos2 %d", rpos2); + + /* Go up till we reach the expected positon. */ + if (rows-rpos2 > 0) { + lndebug("go-up %d", rows-rpos2); + snprintf(seq,64,"\x1b[%dA", rows-rpos2); + abAppend(&ab,seq,strlen(seq)); + } + + /* Set column. */ + col = (pcollen + colpos2) % l->cols; + lndebug("set col %d", 1+col); + if (col) + snprintf(seq,64,"\r\x1b[%dC", col); + else + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + + lndebug("\n"); + l->oldcolpos = colpos2; + + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); +} + +/* Calls the two low level functions refreshSingleLine() or + * refreshMultiLine() according to the selected mode. */ +static void refreshLine(struct linenoiseState *l) { + if (mlmode) + refreshMultiLine(l); + else + refreshSingleLine(l); +} + +/* Insert the character 'c' at cursor current position. + * + * On error writing to the terminal -1 is returned, otherwise 0. */ +int linenoiseEditInsert(struct linenoiseState *l, const char *cbuf, int clen) { + if (l->len+clen <= l->buflen) { + if (l->len == l->pos) { + memcpy(&l->buf[l->pos],cbuf,clen); + l->pos+=clen; + l->len+=clen;; + l->buf[l->len] = '\0'; + if ((!mlmode && promptTextColumnLen(l->prompt,l->plen)+columnPos(l->buf,l->len,l->len) < l->cols && !hintsCallback)) { + /* Avoid a full update of the line in the + * trivial case. */ + if (write(l->ofd,cbuf,clen) == -1) return -1; + } else { + refreshLine(l); + } + } else { + memmove(l->buf+l->pos+clen,l->buf+l->pos,l->len-l->pos); + memcpy(&l->buf[l->pos],cbuf,clen); + l->pos+=clen; + l->len+=clen; + l->buf[l->len] = '\0'; + refreshLine(l); + } + } + return 0; +} + +/* Move cursor on the left. */ +void linenoiseEditMoveLeft(struct linenoiseState *l) { + if (l->pos > 0) { + l->pos -= prevCharLen(l->buf,l->len,l->pos,NULL); + refreshLine(l); + } +} + +/* Move cursor on the right. */ +void linenoiseEditMoveRight(struct linenoiseState *l) { + if (l->pos != l->len) { + l->pos += nextCharLen(l->buf,l->len,l->pos,NULL); + refreshLine(l); + } +} + +/* Move cursor to the start of the line. */ +void linenoiseEditMoveHome(struct linenoiseState *l) { + if (l->pos != 0) { + l->pos = 0; + refreshLine(l); + } +} + +/* Move cursor to the end of the line. */ +void linenoiseEditMoveEnd(struct linenoiseState *l) { + if (l->pos != l->len) { + l->pos = l->len; + refreshLine(l); + } +} + +/* Substitute the currently edited line with the next or previous history + * entry as specified by 'dir'. */ +#define LINENOISE_HISTORY_NEXT 0 +#define LINENOISE_HISTORY_PREV 1 +void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { + if (history_len > 1) { + /* Update the current history entry before to + * overwrite it with the next one. */ + free(history[history_len - 1 - l->history_index]); + history[history_len - 1 - l->history_index] = strdup(l->buf); + /* Show the new entry */ + l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; + if (l->history_index < 0) { + l->history_index = 0; + return; + } else if (l->history_index >= history_len) { + l->history_index = history_len-1; + return; + } + strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); + l->buf[l->buflen-1] = '\0'; + l->len = l->pos = strlen(l->buf); + refreshLine(l); + } +} + +/* Delete the character at the right of the cursor without altering the cursor + * position. Basically this is what happens with the "Delete" keyboard key. */ +void linenoiseEditDelete(struct linenoiseState *l) { + if (l->len > 0 && l->pos < l->len) { + int chlen = nextCharLen(l->buf,l->len,l->pos,NULL); + memmove(l->buf+l->pos,l->buf+l->pos+chlen,l->len-l->pos-chlen); + l->len-=chlen; + l->buf[l->len] = '\0'; + refreshLine(l); + } +} + +/* Backspace implementation. */ +void linenoiseEditBackspace(struct linenoiseState *l) { + if (l->pos > 0 && l->len > 0) { + int chlen = prevCharLen(l->buf,l->len,l->pos,NULL); + memmove(l->buf+l->pos-chlen,l->buf+l->pos,l->len-l->pos); + l->pos-=chlen; + l->len-=chlen; + l->buf[l->len] = '\0'; + refreshLine(l); + } +} + +/* Delete the previosu word, maintaining the cursor at the start of the + * current word. */ +void linenoiseEditDeletePrevWord(struct linenoiseState *l) { + size_t old_pos = l->pos; + size_t diff; + + while (l->pos > 0 && l->buf[l->pos-1] == ' ') + l->pos--; + while (l->pos > 0 && l->buf[l->pos-1] != ' ') + l->pos--; + diff = old_pos - l->pos; + memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); + l->len -= diff; + refreshLine(l); +} + +/* This function is the core of the line editing capability of linenoise. + * It expects 'fd' to be already in "raw mode" so that every key pressed + * will be returned ASAP to read(). + * + * The resulting string is put into 'buf' when the user type enter, or + * when ctrl+d is typed. + * + * The function returns the length of the current buffer. */ +static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) +{ + struct linenoiseState l; + + /* Populate the linenoise state that we pass to functions implementing + * specific editing functionalities. */ + l.ifd = stdin_fd; + l.ofd = stdout_fd; + l.buf = buf; + l.buflen = buflen; + l.prompt = prompt; + l.plen = strlen(prompt); + l.oldcolpos = l.pos = 0; + l.len = 0; + l.cols = getColumns(stdin_fd, stdout_fd); + l.maxrows = 0; + l.history_index = 0; + + /* Buffer starts empty. */ + l.buf[0] = '\0'; + l.buflen--; /* Make sure there is always space for the nulterm */ + + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + linenoiseHistoryAdd(""); + + if (write(l.ofd,prompt,l.plen) == -1) return -1; + while(1) { + int c; + char cbuf[32]; // large enough for any encoding? + int nread; + char seq[3]; + + nread = readCode(l.ifd,cbuf,sizeof(cbuf),&c); + if (nread <= 0) return l.len; + + /* Only autocomplete when the callback is set. It returns < 0 when + * there was an error reading from fd. Otherwise it will return the + * character that should be handled next. */ + if (c == 9 && completionCallback != NULL) { + nread = completeLine(&l,cbuf,sizeof(cbuf),&c); + /* Return on errors */ + if (c < 0) return l.len; + /* Read next character when 0 */ + if (c == 0) continue; + } + + switch(c) { + case ENTER: /* enter */ + history_len--; + free(history[history_len]); + if (mlmode) linenoiseEditMoveEnd(&l); + if (hintsCallback) { + /* Force a refresh without hints to leave the previous + * line as the user typed it after a newline. */ + linenoiseHintsCallback *hc = hintsCallback; + hintsCallback = NULL; + refreshLine(&l); + hintsCallback = hc; + } + return (int)l.len; + case CTRL_C: /* ctrl-c */ + errno = EAGAIN; + return -1; + case BACKSPACE: /* backspace */ + case 8: /* ctrl-h */ + linenoiseEditBackspace(&l); + break; + case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the + line is empty, act as end-of-file. */ + if (l.len > 0) { + linenoiseEditDelete(&l); + } else { + history_len--; + free(history[history_len]); + return -1; + } + break; + case CTRL_T: /* ctrl-t, swaps current character with previous. */ + if (l.pos > 0 && l.pos < l.len) { + int aux = buf[l.pos-1]; + buf[l.pos-1] = buf[l.pos]; + buf[l.pos] = aux; + if (l.pos != l.len-1) l.pos++; + refreshLine(&l); + } + break; + case CTRL_B: /* ctrl-b */ + linenoiseEditMoveLeft(&l); + break; + case CTRL_F: /* ctrl-f */ + linenoiseEditMoveRight(&l); + break; + case CTRL_P: /* ctrl-p */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); + break; + case CTRL_N: /* ctrl-n */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); + break; + case ESC: /* escape sequence */ + /* Read the next two bytes representing the escape sequence. + * Use two calls to handle slow terminals returning the two + * chars at different times. */ + if (read(l.ifd,seq,1) == -1) break; + if (read(l.ifd,seq+1,1) == -1) break; + + /* ESC [ sequences. */ + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + /* Extended escape, read additional byte. */ + if (read(l.ifd,seq+2,1) == -1) break; + if (seq[2] == '~') { + switch(seq[1]) { + case '3': /* Delete key. */ + linenoiseEditDelete(&l); + break; + } + } + } else { + switch(seq[1]) { + case 'A': /* Up */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); + break; + case 'B': /* Down */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); + break; + case 'C': /* Right */ + linenoiseEditMoveRight(&l); + break; + case 'D': /* Left */ + linenoiseEditMoveLeft(&l); + break; + case 'H': /* Home */ + linenoiseEditMoveHome(&l); + break; + case 'F': /* End*/ + linenoiseEditMoveEnd(&l); + break; + } + } + } + + /* ESC O sequences. */ + else if (seq[0] == 'O') { + switch(seq[1]) { + case 'H': /* Home */ + linenoiseEditMoveHome(&l); + break; + case 'F': /* End*/ + linenoiseEditMoveEnd(&l); + break; + } + } + break; + default: + if (linenoiseEditInsert(&l,cbuf,nread)) return -1; + break; + case CTRL_U: /* Ctrl+u, delete the whole line. */ + buf[0] = '\0'; + l.pos = l.len = 0; + refreshLine(&l); + break; + case CTRL_K: /* Ctrl+k, delete from current to end of line. */ + buf[l.pos] = '\0'; + l.len = l.pos; + refreshLine(&l); + break; + case CTRL_A: /* Ctrl+a, go to the start of the line */ + linenoiseEditMoveHome(&l); + break; + case CTRL_E: /* ctrl+e, go to the end of the line */ + linenoiseEditMoveEnd(&l); + break; + case CTRL_L: /* ctrl+l, clear screen */ + linenoiseClearScreen(); + refreshLine(&l); + break; + case CTRL_W: /* ctrl+w, delete previous word */ + linenoiseEditDeletePrevWord(&l); + break; + } + } + return l.len; +} + +/* This special mode is used by linenoise in order to print scan codes + * on screen for debugging / development purposes. It is implemented + * by the linenoise_example program using the --keycodes option. */ +void linenoisePrintKeyCodes(void) { + char quit[4]; + + printf("Linenoise key codes debugging mode.\n" + "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); + if (enableRawMode(STDIN_FILENO) == -1) return; + memset(quit,' ',4); + while(1) { + char c; + int nread; + + nread = read(STDIN_FILENO,&c,1); + if (nread <= 0) continue; + memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ + quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ + if (memcmp(quit,"quit",sizeof(quit)) == 0) break; + + printf("'%c' %02x (%d) (type quit to exit)\n", + isprint((int)c) ? c : '?', (int)c, (int)c); + printf("\r"); /* Go left edge manually, we are in raw mode. */ + fflush(stdout); + } + disableRawMode(STDIN_FILENO); +} + +/* This function calls the line editing function linenoiseEdit() using + * the STDIN file descriptor set in raw mode. */ +static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { + int count; + + if (buflen == 0) { + errno = EINVAL; + return -1; + } + + if (enableRawMode(STDIN_FILENO) == -1) return -1; + count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); + disableRawMode(STDIN_FILENO); + printf("\n"); + return count; +} + +/* This function is called when linenoise() is called with the standard + * input file descriptor not attached to a TTY. So for example when the + * program using linenoise is called in pipe or with a file redirected + * to its standard input. In this case, we want to be able to return the + * line regardless of its length (by default we are limited to 4k). */ +static char *linenoiseNoTTY(void) { + char *line = NULL; + size_t len = 0, maxlen = 0; + + while(1) { + if (len == maxlen) { + if (maxlen == 0) maxlen = 16; + maxlen *= 2; + char *oldval = line; + line = realloc(line,maxlen); + if (line == NULL) { + if (oldval) free(oldval); + return NULL; + } + } + int c = fgetc(stdin); + if (c == EOF || c == '\n') { + if (c == EOF && len == 0) { + free(line); + return NULL; + } else { + line[len] = '\0'; + return line; + } + } else { + line[len] = c; + len++; + } + } +} + +/* The high level function that is the main API of the linenoise library. + * This function checks if the terminal has basic capabilities, just checking + * for a blacklist of stupid terminals, and later either calls the line + * editing function or uses dummy fgets() so that you will be able to type + * something even in the most desperate of the conditions. */ +char *linenoise(const char *prompt) { + char buf[LINENOISE_MAX_LINE]; + int count; + + if (!isatty(STDIN_FILENO)) { + /* Not a tty: read from file / pipe. In this mode we don't want any + * limit to the line size, so we call a function to handle that. */ + return linenoiseNoTTY(); + } else if (isUnsupportedTerm()) { + size_t len; + + printf("%s",prompt); + fflush(stdout); + if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; + len = strlen(buf); + while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + len--; + buf[len] = '\0'; + } + return strdup(buf); + } else { + count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); + if (count == -1) return NULL; + return strdup(buf); + } +} + +/* This is just a wrapper the user may want to call in order to make sure + * the linenoise returned buffer is freed with the same allocator it was + * created with. Useful when the main program is using an alternative + * allocator. */ +void linenoiseFree(void *ptr) { + free(ptr); +} + +/* ================================ History ================================= */ + +/* Free the history, but does not reset it. Only used when we have to + * exit() to avoid memory leaks are reported by valgrind & co. */ +static void freeHistory(void) { + if (history) { + int j; + + for (j = 0; j < history_len; j++) + free(history[j]); + free(history); + } +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +static void linenoiseAtExit(void) { + disableRawMode(STDIN_FILENO); + freeHistory(); +} + +/* This is the API call to add a new entry in the linenoise history. + * It uses a fixed array of char pointers that are shifted (memmoved) + * when the history max length is reached in order to remove the older + * entry and make room for the new one, so it is not exactly suitable for huge + * histories, but will work well for a few hundred of entries. + * + * Using a circular buffer is smarter, but a bit more complex to handle. */ +int linenoiseHistoryAdd(const char *line) { + char *linecopy; + + if (history_max_len == 0) return 0; + + /* Initialization on first call. */ + if (history == NULL) { + history = malloc(sizeof(char*)*history_max_len); + if (history == NULL) return 0; + memset(history,0,(sizeof(char*)*history_max_len)); + } + + /* Don't add duplicated lines. */ + if (history_len && !strcmp(history[history_len-1], line)) return 0; + + /* Add an heap allocated copy of the line in the history. + * If we reached the max length, remove the older line. */ + linecopy = strdup(line); + if (!linecopy) return 0; + if (history_len == history_max_len) { + free(history[0]); + memmove(history,history+1,sizeof(char*)*(history_max_len-1)); + history_len--; + } + history[history_len] = linecopy; + history_len++; + return 1; +} + +/* Set the maximum length for the history. This function can be called even + * if there is already some history, the function will make sure to retain + * just the latest 'len' elements if the new history length value is smaller + * than the amount of items already inside the history. */ +int linenoiseHistorySetMaxLen(int len) { + char **new; + + if (len < 1) return 0; + if (history) { + int tocopy = history_len; + + new = malloc(sizeof(char*)*len); + if (new == NULL) return 0; + + /* If we can't copy everything, free the elements we'll not use. */ + if (len < tocopy) { + int j; + + for (j = 0; j < tocopy-len; j++) free(history[j]); + tocopy = len; + } + memset(new,0,sizeof(char*)*len); + memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); + free(history); + history = new; + } + history_max_len = len; + if (history_len > history_max_len) + history_len = history_max_len; + return 1; +} + +/* Save the history in the specified file. On success 0 is returned + * otherwise -1 is returned. */ +int linenoiseHistorySave(const char *filename) { + mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + FILE *fp; + int j; + + fp = fopen(filename,"w"); + umask(old_umask); + if (fp == NULL) return -1; + chmod(filename,S_IRUSR|S_IWUSR); + for (j = 0; j < history_len; j++) + fprintf(fp,"%s\n",history[j]); + fclose(fp); + return 0; +} + +/* Load the history from the specified file. If the file does not exist + * zero is returned and no operation is performed. + * + * If the file exists and the operation succeeded 0 is returned, otherwise + * on error -1 is returned. */ +int linenoiseHistoryLoad(const char *filename) { + FILE *fp = fopen(filename,"r"); + char buf[LINENOISE_MAX_LINE]; + + if (fp == NULL) return -1; + + while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { + char *p; + + p = strchr(buf,'\r'); + if (!p) p = strchr(buf,'\n'); + if (p) *p = '\0'; + linenoiseHistoryAdd(buf); + } + fclose(fp); + return 0; +} diff --git a/external/linenoise/linenoise.h b/external/linenoise/linenoise.h new file mode 100644 index 00000000..df32133c --- /dev/null +++ b/external/linenoise/linenoise.h @@ -0,0 +1,82 @@ +/* linenoise.h -- VERSION 1.0 + * + * Guerrilla line editing library against the idea that a line editing lib + * needs to be 20,000 lines of C code. + * + * See linenoise.c for more information. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010-2014, Salvatore Sanfilippo + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LINENOISE_H +#define __LINENOISE_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct linenoiseCompletions { + size_t len; + char **cvec; +} linenoiseCompletions; + +typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); +typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); +typedef void(linenoiseFreeHintsCallback)(void *); +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); +void linenoiseSetHintsCallback(linenoiseHintsCallback *); +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); +void linenoiseAddCompletion(linenoiseCompletions *, const char *); + +char *linenoise(const char *prompt); +void linenoiseFree(void *ptr); +int linenoiseHistoryAdd(const char *line); +int linenoiseHistorySetMaxLen(int len); +int linenoiseHistorySave(const char *filename); +int linenoiseHistoryLoad(const char *filename); +void linenoiseClearScreen(void); +void linenoiseSetMultiLine(int ml); +void linenoisePrintKeyCodes(void); + +typedef size_t (linenoisePrevCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len); +typedef size_t (linenoiseNextCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len); +typedef size_t (linenoiseReadCode)(int fd, char *buf, size_t buf_len, int* c); + +void linenoiseSetEncodingFunctions( + linenoisePrevCharLen *prevCharLenFunc, + linenoiseNextCharLen *nextCharLenFunc, + linenoiseReadCode *readCodeFunc); + +#ifdef __cplusplus +} +#endif + +#endif /* __LINENOISE_H */ diff --git a/external/linenoise/makefile b/external/linenoise/makefile new file mode 100644 index 00000000..7993ffcf --- /dev/null +++ b/external/linenoise/makefile @@ -0,0 +1,20 @@ +# bleugh. +# so we can call from our main makefile. + + +.PHONY: all + +all: ../liblinenoise.a + @: + +../liblinenoise.a: linenoise.c.o encodings/utf8.c.o + @echo "# liblinenoise" + @$(AR) rcs ../liblinenoise.a linenoise.c.o encodings/utf8.c.o + +%.c.o: %.c + @echo "# linenoise/$(notdir $<)" + @$(CC) -std=c11 -O3 -c -o $@ $< + + + + diff --git a/makefile b/makefile index 84b861ff..060f6b7c 100644 --- a/makefile +++ b/makefile @@ -85,6 +85,7 @@ endif UTF8REWIND_AR := external/libutf8rewind.a +LINENOISE_AR := external/liblinenoise.a FLXFLAGS += -sysroot $(SYSROOT) --ffi-escape @@ -121,6 +122,9 @@ compile: build test: build @$(OUTPUT) $(FLXFLAGS) -run -o $(TESTBIN) $(TESTSRC) +repl: build + @$(OUTPUT) $(FLXFLAGS) -repl + gltest: build @$(OUTPUT) $(FLXFLAGS) -run -framework GLUT -framework OpenGL -lsdl2 -o $(GLTESTBIN) $(GLTESTSRC) @@ -141,10 +145,10 @@ copylibs: $(FLXSRC) @mv $(FLXLIBLOCATION)/libs $(FLXLIBLOCATION)/flaxlibs -$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) +$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(LINENOISE_AR) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind -llinenoise %.cpp.o: %.cpp @@ -160,6 +164,8 @@ $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(UTF8REWIND_AR): @make -C external/utf8rewind all +$(LINENOISE_AR): + @make -C external/linenoise all # haha clena: clean diff --git a/source/frontend/arguments.cpp b/source/frontend/arguments.cpp index 93dd016c..8ff7be0b 100644 --- a/source/frontend/arguments.cpp +++ b/source/frontend/arguments.cpp @@ -148,6 +148,11 @@ static std::string parseQuotedString(char** argv, int& i) namespace frontend { + std::string getVersion() + { + return FLAX_VERSION_STRING; + } + static std::vector frameworksToLink; static std::vector frameworkSearchPaths; diff --git a/source/include/frontend.h b/source/include/frontend.h index 682c0a2f..d9a17381 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -34,6 +34,7 @@ namespace backend namespace frontend { std::string getParameter(const std::string& arg); + std::string getVersion(); backend::ProgOutputMode getOutputMode(); backend::OptimisationLevel getOptLevel(); diff --git a/source/include/repl.h b/source/include/repl.h new file mode 100644 index 00000000..3fb6244f --- /dev/null +++ b/source/include/repl.h @@ -0,0 +1,11 @@ +// repl.h +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#pragma once +#include "defs.h" + +namespace repl +{ + void start(); +} diff --git a/source/main.cpp b/source/main.cpp index 0b5ecd1c..7713ac40 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -3,6 +3,7 @@ // Licensed under the Apache License Version 2.0. #include "defs.h" +#include "repl.h" #include "errors.h" #include "backend.h" #include "frontend.h" @@ -165,7 +166,7 @@ int main(int argc, char** argv) auto [ input_file, output_file ] = frontend::parseCmdLineOpts(argc, argv); - if(frontend::getIsReplMode()) ; + if(frontend::getIsReplMode()) repl::start(); else compile(input_file, output_file); diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp new file mode 100644 index 00000000..56fcac84 --- /dev/null +++ b/source/repl/driver.cpp @@ -0,0 +1,67 @@ +// driver.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "repl.h" +#include "frontend.h" + +#include "linenoise/linenoise.h" + +namespace repl +{ + static void runCommand(const std::string& s) + { + if(s == "q") exit(0); + else if(s == "help") fprintf(stderr, "no help implemented\n"); + else fprintf(stderr, "invalid command '%s'\n", s.c_str()); + } + + static constexpr const char* PROMPT_STRING = COLOUR_BLUE "*" COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + + void start() + { + printf("flax version %s\n", frontend::getVersion().c_str()); + printf("type :help for help\n\n"); + + linenoiseSetMultiLine(1); + while(char* _line = linenoise(PROMPT_STRING)) + { + std::string line = _line; + linenoiseFree(_line); + + if(line.empty()) + continue; + + if(line[0] == ':') + { + runCommand(line.substr(1)); + printf("\n"); + continue; + } + + + + + + printf("here: %s\n", line.c_str()); + } + } +} + + + + + + + + + + + + + + + + + + From 76d9149baad6550284cabc94a4c078e03a12ac75 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 5 Nov 2019 01:37:38 +0800 Subject: [PATCH 052/129] refactor the file-stuff a bit. error-handling and parsing needs to completely change ): --- source/frontend/collector.cpp | 2 +- source/frontend/file.cpp | 178 ++++++++++++++++------------ source/frontend/parser/toplevel.cpp | 18 +-- source/include/frontend.h | 12 ++ source/include/parser.h | 3 +- source/include/platform.h | 1 + source/include/repl.h | 8 ++ source/platform/platform.cpp | 15 ++- source/repl/driver.cpp | 16 ++- source/repl/parse.cpp | 22 ++++ 10 files changed, 178 insertions(+), 97 deletions(-) create mode 100644 source/repl/parse.cpp diff --git a/source/frontend/collector.cpp b/source/frontend/collector.cpp index 1e0b06ea..a8a14487 100644 --- a/source/frontend/collector.cpp +++ b/source/frontend/collector.cpp @@ -75,7 +75,7 @@ namespace frontend } - state->parsed[file] = parser::parseFile(file, *state); + state->parsed[file] = parser::parseFile(file, frontend::getFileState(file), *state); } } diff --git a/source/frontend/file.cpp b/source/frontend/file.cpp index 52d3f06a..405e6489 100644 --- a/source/frontend/file.cpp +++ b/source/frontend/file.cpp @@ -17,116 +17,142 @@ namespace frontend { - struct FileInnards - { - lexer::TokenList tokens; - std::string_view fileContents; - util::FastInsertVector lines; - std::vector importIndices; - - bool didLex = false; - }; - static util::hash_map fileList; - static FileInnards& readFileIfNecessary(const std::string& fullPath) + + static void getRawLines(const std::string_view& fileContents, bool* crlf, util::FastInsertVector* rawlines) { - // break early if we can + std::string_view view = fileContents; + + bool first = true; + while(true) { - auto it = fileList.find(fullPath); - if(it != fileList.end()) - return it->second; - } + size_t ln = 0; + + if(first || *crlf) + { + ln = view.find("\r\n"); + if(ln != std::string_view::npos && first) + *crlf = true; + } + + if((!first && !*crlf) || (first && !*crlf && ln == std::string_view::npos)) + ln = view.find('\n'); + first = false; - std::string_view fileContents; + if(ln != std::string_view::npos) + { + new (rawlines->getNextSlotAndIncrement()) std::string_view(view.data(), ln + (*crlf ? 2 : 1)); + view.remove_prefix(ln + (*crlf ? 2 : 1)); + } + else + { + break; + } + } + + // account for the case when there's no trailing newline, and we still have some stuff stuck in the view. + if(!view.empty()) { - fileContents = platform::readEntireFile(fullPath); + new (rawlines->getNextSlotAndIncrement()) std::string_view(view.data(), view.length()); } + } + + static void tokenise(lexer::TokenList* ts, bool crlf, const std::string_view& fileContents, + const util::FastInsertVector& lines, Location* pos, std::vector* importIndices) + { + size_t curLine = 0; + size_t curOffset = 0; + + bool flag = true; + size_t i = 0; + + do { + auto type = lexer::getNextToken(lines, &curLine, &curOffset, fileContents, *pos, + ts->getNextSlotAndIncrement(), crlf); + + flag = (type != lexer::TokenType::EndOfFile); + + if(type == lexer::TokenType::Import) + importIndices->push_back(i); + else if(type == lexer::TokenType::Invalid) + error(*pos, "invalid token"); + i++; + } while(flag); + + (*ts)[ts->size() - 1].loc.len = 0; + } + + + + + static void lex(FileInnards* innards, bool crlf, Location* pos) + { + lexer::TokenList& ts = innards->tokens; + tokenise(&ts, crlf, innards->fileContents, innards->lines, pos, &innards->importIndices); + + innards->didLex = true; + } + + FileInnards lexTokensFromString(const std::string& fakename, const std::string_view& fileContents) + { // split into lines bool crlf = false; util::FastInsertVector rawlines; - { - std::string_view view = fileContents; - - bool first = true; - while(true) - { - size_t ln = 0; - - if(first || crlf) - { - ln = view.find("\r\n"); - if(ln != std::string_view::npos && first) - crlf = true; - } - - if((!first && !crlf) || (first && !crlf && ln == std::string_view::npos)) - ln = view.find('\n'); - - first = false; - - if(ln != std::string_view::npos) - { - new (rawlines.getNextSlotAndIncrement()) std::string_view(view.data(), ln + (crlf ? 2 : 1)); - view.remove_prefix(ln + (crlf ? 2 : 1)); - } - else - { - break; - } - } - - // account for the case when there's no trailing newline, and we still have some stuff stuck in the view. - if(!view.empty()) - { - new (rawlines.getNextSlotAndIncrement()) std::string_view(view.data(), view.length()); - } - } + getRawLines(fileContents, &crlf, &rawlines); Location pos; - FileInnards& innards = fileList[fullPath]; + FileInnards innards; { - pos.fileID = getFileIDFromFilename(fullPath); + pos.fileID = getFileIDFromFilename(fakename); innards.fileContents = fileContents; innards.lines = std::move(rawlines); } - lexer::TokenList& ts = innards.tokens; - { - size_t curLine = 0; - size_t curOffset = 0; - - bool flag = true; - size_t i = 0; - - do { - auto type = lexer::getNextToken(innards.lines, &curLine, &curOffset, innards.fileContents, pos, ts.getNextSlotAndIncrement(), crlf); + lex(&innards, crlf, &pos); + return innards; + } - flag = (type != lexer::TokenType::EndOfFile); + static FileInnards& readFileIfNecessary(const std::string& fullPath) + { + // break early if we can + { + auto it = fileList.find(fullPath); + if(it != fileList.end()) + return it->second; + } - if(type == lexer::TokenType::Import) - innards.importIndices.push_back(i); + std::string_view fileContents = platform::readEntireFile(fullPath); - else if(type == lexer::TokenType::Invalid) - error(pos, "invalid token"); - i++; + // split into lines + bool crlf = false; + util::FastInsertVector rawlines; + getRawLines(fileContents, &crlf, &rawlines); - } while(flag); + Location pos; + FileInnards& innards = fileList[fullPath]; + { + pos.fileID = getFileIDFromFilename(fullPath); - ts[ts.size() - 1].loc.len = 0; + innards.fileContents = fileContents; + innards.lines = std::move(rawlines); } - innards.didLex = true; + lex(&innards, crlf, &pos); return innards; } + FileInnards& getFileState(const std::string& name) + { + return readFileIfNecessary(name); + } lexer::TokenList& getFileTokens(const std::string& fullPath) { diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index 4e26305f..d9b4a65e 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -44,10 +44,11 @@ namespace parser } - static std::pair> parseModuleName(const std::string& fullpath) + static std::pair> parseModuleName(const std::string& fullname, + const frontend::FileInnards& fileinnards) { using TT = lexer::TokenType; - auto tokens = frontend::getFileTokens(fullpath); + const auto& tokens = fileinnards.tokens; std::vector path; @@ -88,7 +89,7 @@ namespace parser } if(path.empty()) - path = { frontend::removeExtensionFromFilename(frontend::getFilenameFromPath(fullpath)) }; + path = { frontend::removeExtensionFromFilename(frontend::getFilenameFromPath(fullname)) }; return { path.back(), util::take(path, path.size() - 1) }; } @@ -296,12 +297,11 @@ namespace parser return root; } - ParsedFile parseFile(const std::string& filename, frontend::CollectorState& cs) + ParsedFile parseFile(const std::string& fullname, const frontend::FileInnards& file, frontend::CollectorState& cs) { - auto full = frontend::getFullPathOfFile(filename); - const TokenList& tokens = frontend::getFileTokens(full); + const TokenList& tokens = file.tokens; auto state = State(tokens); - state.currentFilePath = full; + state.currentFilePath = fullname; // copy this stuff over. state.binaryOps = cs.binaryOps; @@ -310,12 +310,12 @@ namespace parser state.cState = &cs; - auto [ modname, modpath ] = parseModuleName(full); + auto [ modname, modpath ] = parseModuleName(fullname, file); auto toplevel = parseTopLevel(state, ""); return ParsedFile { - filename, + fullname, modname, modpath, toplevel, diff --git a/source/include/frontend.h b/source/include/frontend.h index d9a17381..4c84996b 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -92,6 +92,18 @@ namespace frontend std::pair parseCmdLineOpts(int argc, char** argv); + struct FileInnards + { + lexer::TokenList tokens; + std::string_view fileContents; + util::FastInsertVector lines; + std::vector importIndices; + + bool didLex = false; + }; + + FileInnards& getFileState(const std::string& name); + FileInnards lexTokensFromString(const std::string& fakename, const std::string_view& str); std::string getPathFromFile(const std::string& path); std::string getFilenameFromPath(const std::string& path); diff --git a/source/include/parser.h b/source/include/parser.h index 7dfa3bb0..5c5c214b 100644 --- a/source/include/parser.h +++ b/source/include/parser.h @@ -14,6 +14,7 @@ namespace ast namespace frontend { struct ImportThing; + struct FileInnards; struct CollectorState; } @@ -46,7 +47,7 @@ namespace parser size_t parseOperatorDecl(const lexer::TokenList& tokens, size_t i, int* kind, CustomOperatorDecl* out); std::vector parseImports(const std::string& filename, const lexer::TokenList& tokens); - ParsedFile parseFile(const std::string& filename, frontend::CollectorState& cs); + ParsedFile parseFile(const std::string& filename, const frontend::FileInnards& file, frontend::CollectorState& cs); } diff --git a/source/include/platform.h b/source/include/platform.h index 83a0217e..6a37d095 100644 --- a/source/include/platform.h +++ b/source/include/platform.h @@ -76,6 +76,7 @@ namespace platform bool checkFileExists(const std::string& path); std::string_view readEntireFile(const std::string& path); + void cachePreExistingFile(const std::string& path, const std::string& contents); std::string getFullPath(const std::string& partial); diff --git a/source/include/repl.h b/source/include/repl.h index 3fb6244f..e14bd4d7 100644 --- a/source/include/repl.h +++ b/source/include/repl.h @@ -8,4 +8,12 @@ namespace repl { void start(); + void processLine(const std::string& line); + + + template + static void error(const std::string& fmt, Args&&... args) + { + fprintf(stderr, "%serror:%s %s\n", COLOUR_RED_BOLD, COLOUR_RESET, zpr::sprint(fmt, args...).c_str()); + } } diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 35360d35..9d5d28f6 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -183,8 +183,19 @@ namespace platform #endif } + + static util::hash_map cachedFileContents; + + void cachePreExistingFile(const std::string& path, const std::string& contents) + { + cachedFileContents[path] = contents; + } + std::string_view readEntireFile(const std::string& path) { + if(auto it = cachedFileContents.find(path); it != cachedFileContents.end()) + return it->second; + // first, get the size of the file size_t fileLength = getFileSize(path); @@ -235,12 +246,14 @@ namespace platform perror("there was an error reading the file"); error("expected %d bytes, but read only %d", fileLength, didRead); } + + cachedFileContents[path] = std::string(contents, fileLength); } iceAssert(contents); closeFile(fd); - return std::string_view(contents, fileLength); + return cachedFileContents[path]; } filehandle_t openFile(const char* name, int mode, int flags) diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 56fcac84..61ac2e39 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -12,15 +12,16 @@ namespace repl static void runCommand(const std::string& s) { if(s == "q") exit(0); - else if(s == "help") fprintf(stderr, "no help implemented\n"); - else fprintf(stderr, "invalid command '%s'\n", s.c_str()); + else if(s == "help") repl::error("no help implemented. ggwp."); + else repl::error("invalid command '%s'.", s); } - static constexpr const char* PROMPT_STRING = COLOUR_BLUE "*" COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* PROMPT_STRING = COLOUR_BLUE " * " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* CONTINUATION_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; void start() { - printf("flax version %s\n", frontend::getVersion().c_str()); + printf("flax repl -- version %s\n", frontend::getVersion().c_str()); printf("type :help for help\n\n"); linenoiseSetMultiLine(1); @@ -39,11 +40,8 @@ namespace repl continue; } - - - - - printf("here: %s\n", line.c_str()); + processLine(line); + printf("\n"); } } } diff --git a/source/repl/parse.cpp b/source/repl/parse.cpp new file mode 100644 index 00000000..7856eddc --- /dev/null +++ b/source/repl/parse.cpp @@ -0,0 +1,22 @@ +// parse.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "repl.h" +#include "parser.h" +#include "frontend.h" + +namespace repl +{ + void processLine(const std::string& line) + { + frontend::CollectorState collector; + + // lex. + platform::cachePreExistingFile("", line); + auto lexResult = frontend::lexTokensFromString("", line); + + // parse + auto parseResult = parser::parseFile("", lexResult, collector); + } +} From 18b4ddf901c0f828bf988cb8b4177e5adde64be2 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 5 Nov 2019 15:06:59 +0800 Subject: [PATCH 053/129] PResult nonsense, attempt #1 --- source/frontend/file.cpp | 5 + source/frontend/lexer.cpp | 6 +- source/frontend/parser/expr.cpp | 201 +++++++++++++--------------- source/frontend/parser/function.cpp | 4 +- source/frontend/parser/misc.cpp | 15 --- source/frontend/parser/toplevel.cpp | 6 +- source/frontend/parser/type.cpp | 2 +- source/include/defs.h | 2 + source/include/errors.h | 11 -- source/include/frontend.h | 1 + source/include/parser_internal.h | 146 +++++++++++++++++++- source/include/utils.h | 12 ++ source/platform/platform.cpp | 3 + source/repl/parse.cpp | 8 +- source/typecheck/special.cpp | 2 +- source/typecheck/toplevel.cpp | 2 +- 16 files changed, 277 insertions(+), 149 deletions(-) diff --git a/source/frontend/file.cpp b/source/frontend/file.cpp index 405e6489..965bba85 100644 --- a/source/frontend/file.cpp +++ b/source/frontend/file.cpp @@ -167,6 +167,11 @@ namespace frontend static std::vector fileNames { "null" }; static util::hash_map existingNames; + void cachePreExistingFilename(const std::string& name) + { + fileNames.push_back(name); + } + const std::string& getFilenameFromID(size_t fileID) { iceAssert(fileID > 0 && fileID < fileNames.size()); diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index 45dcaa2a..54d87886 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -383,7 +383,7 @@ namespace lexer } else if(hasPrefix(stream, "*/")) { - unexpected(tok.loc, "'*/'"); + error(tok.loc, "unexpected '*/'"); } @@ -654,7 +654,7 @@ namespace lexer { if(i + 1 == stream.size()) { - unexpected(pos, "end of input"); + error(pos, "unexpected end of input"); } else if(stream[i + 1] == '"') { @@ -672,7 +672,7 @@ namespace lexer (*line)++; if(*line == lines.size()) - unexpected(pos, "end of input"); + error(pos, "unexpected end of input"); i = 0; diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index dea6909d..9b3908b7 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -17,7 +17,7 @@ namespace parser { using TT = lexer::TokenType; - Stmt* parseStmtWithAccessSpec(State& st) + PResult parseStmtWithAccessSpec(State& st) { iceAssert(st.front() == TT::Public || st.front() == TT::Private || st.front() == TT::Internal); auto vis = VisibilityLevel::Invalid; @@ -30,33 +30,34 @@ namespace parser } st.pop(); - auto stmt = parseStmt(st, /* allowExprs: */ false); - if(auto fd = dcast(FuncDefn, stmt)) - fd->visibility = vis; + return parseStmt(st, /* allowExprs: */ false).mutate([&](auto stmt) -> void { + if(auto fd = dcast(FuncDefn, stmt)) + fd->visibility = vis; - else if(auto ffd = dcast(ForeignFuncDefn, stmt)) - ffd->visibility = vis; + else if(auto ffd = dcast(ForeignFuncDefn, stmt)) + ffd->visibility = vis; - else if(auto vd = dcast(VarDefn, stmt)) - vd->visibility = vis; + else if(auto vd = dcast(VarDefn, stmt)) + vd->visibility = vis; - else if(auto td = dcast(TypeDefn, stmt)) - td->visibility = vis; + else if(auto td = dcast(TypeDefn, stmt)) + td->visibility = vis; - else - error(st, "access specifier cannot be applied to this statement"); - - return stmt; + else + error(st, "access specifier cannot be applied to this statement"); + }); } - Stmt* parseStmt(State& st, bool allowExprs) + PResult parseStmt(State& st, bool allowExprs) { if(!st.hasTokens()) unexpected(st, "end of file"); st.skipWS(); - std::function checkMethodModifiers = [&st, &checkMethodModifiers](bool mut, bool virt, bool ovrd) -> Stmt* { + std::function (bool, bool, bool)> checkMethodModifiers = [&st, &checkMethodModifiers] + (bool mut, bool virt, bool ovrd) -> PResult + { if(st.front() == TT::Mutable) { if(mut) error(st.loc(), "duplicate 'mut' modifier"); @@ -86,12 +87,11 @@ namespace parser } else { - auto ret = parseFunction(st); - ret->isVirtual = virt; - ret->isOverride = ovrd; - ret->isMutating = mut; - - return ret; + return PResult(parseFunction(st)).mutate([&](auto ret) -> void { + ret->isVirtual = virt; + ret->isOverride = ovrd; + ret->isMutating = mut; + }); } }; @@ -99,35 +99,36 @@ namespace parser if(tok != TT::EndOfFile) { auto attrs = parseAttributes(st); - auto enforceAttrs = [&attrs](Stmt* ret, const AttribSet& allowed = AttribSet::of(attr::NONE)) -> Stmt* { + auto enforceAttrs = [&attrs](const PResult& ret, const AttribSet& allowed = AttribSet::of(attr::NONE)) -> PResult { using namespace attr; - // there's probably a better way to do this, but bleugh - if((attrs.flags & RAW) && !(allowed.flags & RAW)) - error(ret, "unsupported attribute '@raw' on %s", ret->readableName); + return ret.map([&allowed, &attrs](auto ret) -> auto { + // there's probably a better way to do this, but bleugh + if((attrs.flags & RAW) && !(allowed.flags & RAW)) + error(ret, "unsupported attribute '@raw' on %s", ret->readableName); - if((attrs.flags & NO_MANGLE) && !(allowed.flags & NO_MANGLE)) - error(ret, "unsupported attribute '@nomangle' on %s", ret->readableName); + if((attrs.flags & NO_MANGLE) && !(allowed.flags & NO_MANGLE)) + error(ret, "unsupported attribute '@nomangle' on %s", ret->readableName); - if((attrs.flags & FN_ENTRYPOINT) && !(allowed.flags & FN_ENTRYPOINT)) - error(ret, "unsupported attribute '@entry' on %s", ret->readableName); + if((attrs.flags & FN_ENTRYPOINT) && !(allowed.flags & FN_ENTRYPOINT)) + error(ret, "unsupported attribute '@entry' on %s", ret->readableName); - if((attrs.flags & PACKED) && !(allowed.flags & PACKED)) - error(ret, "unsupported attribute '@packed' on %s", ret->readableName); + if((attrs.flags & PACKED) && !(allowed.flags & PACKED)) + error(ret, "unsupported attribute '@packed' on %s", ret->readableName); - // here let's check the arguments and stuff for default attributes. - // note: due to poor API design on my part, if there is no attribute with that name then ::get() - // returns an empty UA, which has a blank name -- so we check that instead. + // here let's check the arguments and stuff for default attributes. + // note: due to poor API design on my part, if there is no attribute with that name then ::get() + // returns an empty UA, which has a blank name -- so we check that instead. - if(auto ua = attrs.get("compiler_support"); !ua.name.empty() && ua.args.size() != 1) - error(ret, "@compiler_support requires exactly one argument"); + if(auto ua = attrs.get("compiler_support"); !ua.name.empty() && ua.args.size() != 1) + error(ret, "@compiler_support requires exactly one argument"); - // actually that's it - - ret->attrs = attrs; - return ret; + // actually that's it + ret->attrs = attrs; + return ret; + }); }; // handle the things that are OK to appear anywhere first: @@ -214,85 +215,68 @@ namespace parser // in the event that it wasn't allowed at top-level. - Stmt* ret = 0; - switch(tok.type) - { - case TT::If: - ret = parseIfStmt(st); - break; + auto ret = [&st, &tok, &allowExprs]() -> PResult { + switch(tok.type) + { + case TT::Return: + return parseReturn(st); - case TT::Else: - error(st, "cannot have 'else' without preceeding 'if'"); + case TT::If: + return parseIfStmt(st); - case TT::Return: - ret = parseReturn(st); - break; + case TT::Else: + error(st, "cannot have 'else' without preceeding 'if'"); - case TT::Do: [[fallthrough]]; - case TT::While: - ret = parseWhileLoop(st); - break; + case TT::Do: [[fallthrough]]; + case TT::While: + return parseWhileLoop(st); - case TT::For: - ret = parseForLoop(st); - break; + case TT::For: + return parseForLoop(st); - case TT::Break: - ret = parseBreak(st); - break; + case TT::Break: + return parseBreak(st); - case TT::Continue: - ret = parseContinue(st); - break; + case TT::Continue: + return parseContinue(st); - case TT::Dealloc: - ret = parseDealloc(st); - break; + case TT::Dealloc: + return parseDealloc(st); - case TT::Defer: - ret = parseDefer(st); - break; + case TT::Defer: + return parseDefer(st); - default: { - if(st.isInStructBody() && tok.type == TT::Identifier) - { - if(tok.str() == "init") - { - ret = parseInitFunction(st); - break; - } - else if(tok.str() == "deinit") + default: { + if(st.isInStructBody() && tok.type == TT::Identifier) { - ret = parseDeinitFunction(st); - break; + if(tok.str() == "init") + { + return parseInitFunction(st); + } + else if(tok.str() == "deinit") + { + return parseDeinitFunction(st); + } + else if(tok.str() == "copy" || tok.str() == "move") + { + return parseCopyOrMoveInitFunction(st, tok.str()); + } } - else if(tok.str() == "copy" || tok.str() == "move") - { - ret = parseCopyOrMoveInitFunction(st, tok.str()); - break; - } - } - // we want to error on invalid tokens first. so, we parse the expression regardless, - // then if they're not allowed we error. - auto expr = parseExpr(st); - - if(!allowExprs) error(expr, "expressions are not allowed at the top-level"); - else ret = expr; - - break; + // we want to error on invalid tokens first. so, we parse the expression regardless, + // then if they're not allowed we error. + return PResult(parseExpr(st)).mutate([&](auto expr) -> void { + if(!allowExprs) + error(expr, "expressions are not allowed at the top-level"); + }); + } } - } + }(); - iceAssert(ret); - if(!st.isInFunctionBody() && !st.isInStructBody()) - { - error(ret, "%s is not allowed at the top-level", ret->readableName); - } - else - { - return ret; - } + return ret.mutate([&](auto ret) { + if(!st.isInFunctionBody() && !st.isInStructBody()) + error(ret, "%s is not allowed at the top-level", ret->readableName); + }); } unexpected(st.loc(), "end of file"); @@ -703,6 +687,7 @@ namespace parser else { unexpected(st.loc(), "'$' in non-subscript context"); + return nullptr; // PRESULT FIXUP } } @@ -1028,6 +1013,8 @@ namespace parser // todo: ++ and --. error("enotsup"); } + + return nullptr; // PRESULT FIXUP } @@ -1114,7 +1101,7 @@ namespace parser ret->actual = parseBracedBlock(st); else - ret->actual = parseStmt(st); + ret->actual = parseStmt(st).val(); return ret; } diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index cb271401..433a63f9 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -391,7 +391,7 @@ namespace parser st.skipWS(); while(st.front() != TT::RBrace) { - auto stmt = parseStmt(st); + auto stmt = parseStmt(st).val(); if(auto defer = dcast(DeferredStmt, stmt)) ret->deferredStatements.push_back(defer); @@ -421,7 +421,7 @@ namespace parser else if(st.front() == TT::FatRightArrow) { Block* ret = util::pool(st.eat().loc); - ret->statements.push_back(parseStmt(st)); + ret->statements.push_back(parseStmt(st).val()); ret->closingBrace = st.loc(); ret->isArrow = true; diff --git a/source/frontend/parser/misc.cpp b/source/frontend/parser/misc.cpp index bc74bab0..e210a121 100644 --- a/source/frontend/parser/misc.cpp +++ b/source/frontend/parser/misc.cpp @@ -291,21 +291,6 @@ namespace parser } } -void expected(const Location& loc, std::string a, std::string b) -{ - error(loc, "expected %s, found '%s' instead", a, b); -} - -void expectedAfter(const Location& loc, std::string a, std::string b, std::string c) -{ - error(loc, "expected %s after %s, found '%s' instead", a, b, c); -} - -void unexpected(const Location& loc, std::string a) -{ - error(loc, "unexpected %s", a); -} - diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index d9b4a65e..a83136ad 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -177,7 +177,7 @@ namespace parser case TT::Attr_NoMangle: { st.pop(); - auto stmt = parseStmt(st); + auto stmt = parseStmt(st).val(); if(!dcast(FuncDefn, stmt) && !dcast(VarDefn, stmt)) error(st, "attribute '@nomangle' can only be applied on function and variable declarations"); @@ -200,7 +200,7 @@ namespace parser case TT::Attr_EntryFn: { st.pop(); - auto stmt = parseStmt(st); + auto stmt = parseStmt(st).val(); if(auto fd = dcast(FuncDefn, stmt)) fd->attrs.set(attr::FN_ENTRYPOINT); @@ -274,7 +274,7 @@ namespace parser st.operatorsStillValid = false; st.nativeWordSizeStillValid = false; - root->statements.push_back(parseStmt(st, /* allowExprs: */ false)); + root->statements.push_back(parseStmt(st, /* allowExprs: */ false).val()); } break; } diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index d4f0c1e0..890db249 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -613,7 +613,7 @@ namespace parser iceAssert(st.front() == TT::Static); st.eat(); - auto stmt = parseStmt(st); + auto stmt = parseStmt(st).val(); if(dcast(FuncDefn, stmt) || dcast(VarDefn, stmt)) return util::pool(stmt); diff --git a/source/include/defs.h b/source/include/defs.h index cb831bd0..5e068dc0 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -254,6 +254,8 @@ namespace util SimpleError* make_SimpleError(const Location& l, const std::string& m, MsgType t = MsgType::Error); OverloadError* make_OverloadError(SimpleError* se, MsgType t = MsgType::Error); ExampleMsg* make_ExampleMsg(const std::string& eg, MsgType t = MsgType::Note); + + } diff --git a/source/include/errors.h b/source/include/errors.h index ed413bf8..31591c77 100644 --- a/source/include/errors.h +++ b/source/include/errors.h @@ -24,17 +24,6 @@ inline void debuglogln(const char* s, Ts&&... ts) } -// error shortcuts - -// Expected $, found '$' instead -[[noreturn]] void expected(const Location& loc, std::string, std::string); - -// Expected $ after $, found '$' instead -[[noreturn]] void expectedAfter(const Location& loc, std::string, std::string, std::string); - -// Unexpected $ -[[noreturn]] void unexpected(const Location& loc, std::string); - #define INTUNSPEC_TYPE_STRING "int" #define UINTUNSPEC_TYPE_STRING "uint" diff --git a/source/include/frontend.h b/source/include/frontend.h index 4c84996b..24d26335 100644 --- a/source/include/frontend.h +++ b/source/include/frontend.h @@ -110,6 +110,7 @@ namespace frontend std::string getFullPathOfFile(const std::string& partial); std::string removeExtensionFromFilename(const std::string& name); + void cachePreExistingFilename(const std::string& name); std::string getFileContents(const std::string& fullPath); const std::string& getFilenameFromID(size_t fileID); size_t getFileIDFromFilename(const std::string& name); diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 39d50608..1d7a3146 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -250,13 +250,155 @@ namespace parser const lexer::TokenList& tokens; }; + + // like TCResult i guess. + template + struct PResult + { + PResult(T* val) + { + this->state = 0; + this->value = val; + } + + explicit PResult(ErrorMsg* err, bool needsmore = false) + { + this->error = err; + this->state = (needsmore ? 2 : 1); + } + + + PResult(const PResult& r) + { + this->state = r.state; + if(this->state == 0) this->value = r.value; + else this->error = r.error; + } + + PResult(PResult&& r) noexcept + { + this->state = r.state; + r.state = -1; + + if(this->state == 0) this->value = std::move(r.value); + else this->error = std::move(r.error); + } + + PResult& operator = (const PResult& r) + { + PResult tmp(r); + *this = std::move(tmp); + return *this; + } + + PResult& operator = (PResult&& r) noexcept + { + if(&r != this) + { + this->state = r.state; + r.state = -1; + + if(this->state == 0) this->value = std::move(r.value); + else this->error = std::move(r.error); + } + + return *this; + } + + // implicit conversion operator. + template >> + operator A() const + { + if(this->state == 0) return PResult(this->value); + else return PResult(this->error); + } + + + + + template + PResult mutate(const F& fn) + { + if(this->state == 0) fn(this->value); + return *this; + } + + template >> + PResult map(const F& fn) const + { + if(this->state > 0) return PResult(this->error); + else return PResult(fn(this->value)); + } + + template ::value_t> + PResult flatmap(const F& fn) const + { + if(this->state > 0) return PResult(this->error); + else return fn(this->value); + } + + ErrorMsg* err() const + { + if(this->state < 1) _error_and_exit("not error\n"); + else return this->error; + } + + T* val() const + { + if(this->state != 0) _error_and_exit("no value\n"); + else return this->value; + } + + bool isError() const + { + return this->state > 0; + } + + bool hasValue() const + { + return this->state == 0; + } + + + using value_t = T; + + private: + // 0 = result, 1 = error, 2 = needsmoretokens + int state; + + union { + T* value; + ErrorMsg* error; + }; + }; + + // Expected $, found '$' instead + [[noreturn]] inline void expected(const Location& loc, std::string a, std::string b) + { + error(loc, "expected %s, found '%s' instead", a, b); + } + + // Expected $ after $, found '$' instead + [[noreturn]] inline void expectedAfter(const Location& loc, std::string a, std::string b, std::string c) + { + error(loc, "expected %s after %s, found '%s' instead", a, b, c); + } + + // Unexpected $ + [[noreturn]] inline void unexpected(const Location& loc, std::string a) + { + error(loc, "unexpected %s", a); + } + + + std::string parseStringEscapes(const Location& loc, const std::string& str); std::string parseOperatorTokens(State& st); pts::Type* parseType(State& st); ast::Expr* parseExpr(State& st); - ast::Stmt* parseStmt(State& st, bool allowExprs = true); + PResult parseStmt(State& st, bool allowExprs = true); ast::DeferredStmt* parseDefer(State& st); @@ -265,7 +407,7 @@ namespace parser ast::ReturnStmt* parseReturn(State& st); ast::ImportStmt* parseImport(State& st); ast::FuncDefn* parseFunction(State& st); - ast::Stmt* parseStmtWithAccessSpec(State& st); + PResult parseStmtWithAccessSpec(State& st); ast::ForeignFuncDefn* parseForeignFunction(State& st); ast::OperatorOverloadDefn* parseOperatorOverload(State& st); diff --git a/source/include/utils.h b/source/include/utils.h index 179c92c4..166962aa 100644 --- a/source/include/utils.h +++ b/source/include/utils.h @@ -312,3 +312,15 @@ namespace util + + + + + + + + + + + + diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 9d5d28f6..3fb7231d 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -189,6 +189,9 @@ namespace platform void cachePreExistingFile(const std::string& path, const std::string& contents) { cachedFileContents[path] = contents; + + // this will give cache a new id for us. (over there) + frontend::cachePreExistingFilename(path); } std::string_view readEntireFile(const std::string& path) diff --git a/source/repl/parse.cpp b/source/repl/parse.cpp index 7856eddc..0d3e260c 100644 --- a/source/repl/parse.cpp +++ b/source/repl/parse.cpp @@ -10,13 +10,15 @@ namespace repl { void processLine(const std::string& line) { + std::string replName = ""; + frontend::CollectorState collector; // lex. - platform::cachePreExistingFile("", line); - auto lexResult = frontend::lexTokensFromString("", line); + platform::cachePreExistingFile(replName, line); + auto lexResult = frontend::lexTokensFromString(replName, line); // parse - auto parseResult = parser::parseFile("", lexResult, collector); + auto parseResult = parser::parseFile(replName, lexResult, collector); } } diff --git a/source/typecheck/special.cpp b/source/typecheck/special.cpp index 05b0e039..8b8d38d3 100644 --- a/source/typecheck/special.cpp +++ b/source/typecheck/special.cpp @@ -23,7 +23,7 @@ TCResult ast::MutabilityTypeExpr::typecheck(sst::TypecheckState* fs, fir::Type* TCResult ast::ImportStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) { // nothing to check?? - unexpected(this->loc, "import statement"); + error(this->loc, "unexpected import statement"); } TCResult ast::SplatOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index f6ae6904..1285ab78 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -130,7 +130,7 @@ namespace sst { // probably a class or something conflict: - SimpleError::make(def->loc, "duplicate definition of '%s'", def->id.name) + SimpleError::make(def->loc, "duplicate definition of %s '%s'", def->readableName, def->id.name) ->append(SimpleError::make(MsgType::Note, ot->loc, "conflicting definition was here:")) ->postAndQuit(); } From 5846f719dfdd40baaf82e2a6260dbbbedb1b4078 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 5 Nov 2019 20:52:50 +0800 Subject: [PATCH 054/129] very basic multi-line functionality --- source/frontend/import.cpp | 2 +- source/frontend/parser/controlflow.cpp | 56 +++++++++++++++----------- source/frontend/parser/expr.cpp | 10 ++--- source/frontend/parser/function.cpp | 41 +++++++++++-------- source/frontend/parser/misc.cpp | 2 +- source/frontend/parser/operators.cpp | 2 +- source/frontend/parser/type.cpp | 2 +- source/include/defs.h | 11 ++++- source/include/parser_internal.h | 47 +++++++++++++++++---- source/include/repl.h | 3 +- source/platform/backtrace.cpp | 5 ++- source/repl/driver.cpp | 35 +++++++++++----- source/repl/parse.cpp | 56 ++++++++++++++++++++++++-- 13 files changed, 200 insertions(+), 72 deletions(-) diff --git a/source/frontend/import.cpp b/source/frontend/import.cpp index 1411b91b..145e3575 100644 --- a/source/frontend/import.cpp +++ b/source/frontend/import.cpp @@ -84,7 +84,7 @@ namespace parser for(size_t i = 0; i < tokens.size(); i++) { const Token& tok = tokens[i]; - if(tok == TT::Import || ((tok == TT::Public || tok == TT::Private) && tokens[i + 1] == TT::Import)) + if(tok == TT::Import || ((tok == TT::Public || tok == TT::Private) && i + 1 < tokens.size() && tokens[i + 1] == TT::Import)) { bool pub = false; if(tok == TT::Public) i++, pub = true; diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index 8c393725..d1b8e56d 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -10,9 +10,11 @@ namespace parser { using TT = lexer::TokenType; - ast::Stmt* parseIfStmt(State& st) + using namespace ast; + + Stmt* parseIfStmt(State& st) { - using Case = ast::IfStmt::Case; + using Case = IfStmt::Case; auto tok_if = st.eat(); iceAssert(tok_if == TT::If || tok_if == TT::Directive_If); @@ -24,7 +26,7 @@ namespace parser // braces are compulsory // parentheses around the condition are not - ast::Block* elseCase = 0; + Block* elseCase = 0; std::vector cases; cases.push_back(Case()); @@ -59,7 +61,7 @@ namespace parser st.eat(); } - cases.back().body = parseBracedBlock(st); + cases.back().body = parseBracedBlock(st).val(); } @@ -98,13 +100,13 @@ namespace parser st.eat(); } - c.body = parseBracedBlock(st); + c.body = parseBracedBlock(st).val(); cases.push_back(c); } else if(st.frontAfterWS() == TT::LBrace || st.frontAfterWS() == TT::FatRightArrow) { // ok, parse an else - elseCase = parseBracedBlock(st); + elseCase = parseBracedBlock(st).val(); break; } else @@ -117,7 +119,7 @@ namespace parser if(isStaticIf) { // compile-time if - auto ret = util::pool(tok_if.loc); + auto ret = util::pool(tok_if.loc); ret->cases = cases; ret->elseCase = elseCase; @@ -132,7 +134,7 @@ namespace parser else { // normal runtime if - auto ret = util::pool(tok_if.loc); + auto ret = util::pool(tok_if.loc); ret->cases = cases; ret->elseCase = elseCase; @@ -140,11 +142,11 @@ namespace parser } } - ast::ReturnStmt* parseReturn(State& st) + ReturnStmt* parseReturn(State& st) { iceAssert(st.front() == TT::Return); - auto ret = util::pool(st.loc()); + auto ret = util::pool(st.loc()); st.eat(); // check what's the next thing. problem: return has an *optional* value @@ -166,7 +168,7 @@ namespace parser - ast::Stmt* parseForLoop(State& st) + Stmt* parseForLoop(State& st) { iceAssert(st.front() == TT::For); st.eat(); @@ -174,7 +176,7 @@ namespace parser if(!util::match(st.front(), TT::Identifier, TT::LParen)) expectedAfter(st.loc(), "'(' or identifier", "'for'", st.front().str()); - auto ret = util::pool(st.ploc()); + auto ret = util::pool(st.ploc()); if(st.front() == TT::LParen) { @@ -215,13 +217,13 @@ namespace parser ret->array = parseExpr(st); st.skipWS(); - ret->body = parseBracedBlock(st); + ret->body = parseBracedBlock(st).val(); return ret; } - ast::WhileLoop* parseWhileLoop(State& st) + PResult parseWhileLoop(State& st) { // 1. do { } -- body = block, cond = 0, doVariant = true // 2. while x { } -- body = block, cond = x, doVariant = false @@ -229,8 +231,8 @@ namespace parser auto loc = st.loc(); - ast::Expr* cond = 0; - ast::Block* body = 0; + Expr* cond = 0; + Block* body = 0; bool isdo = false; iceAssert(st.front() == TT::While || st.front() == TT::Do); @@ -241,7 +243,11 @@ namespace parser cond = parseExpr(st); st.skipWS(); - body = parseBracedBlock(st); + auto res = parseBracedBlock(st); + if(res.isError()) + return PResult::copyError(res); + + body = res.val(); } else { @@ -249,7 +255,11 @@ namespace parser st.eat(); - body = parseBracedBlock(st); + auto res = parseBracedBlock(st); + if(res.isError()) + return PResult::copyError(res); + + body = res.val(); if(st.front() == TT::While) { @@ -263,7 +273,7 @@ namespace parser } } - auto ret = util::pool(loc); + auto ret = util::pool(loc); ret->isDoVariant = isdo; ret->body = body; ret->cond = cond; @@ -272,16 +282,16 @@ namespace parser } - ast::Stmt* parseBreak(State& st) + Stmt* parseBreak(State& st) { iceAssert(st.front() == TT::Break); - return util::pool(st.eat().loc); + return util::pool(st.eat().loc); } - ast::Stmt* parseContinue(State& st) + Stmt* parseContinue(State& st) { iceAssert(st.front() == TT::Continue); - return util::pool(st.eat().loc); + return util::pool(st.eat().loc); } } diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 9b3908b7..f6bea076 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -51,7 +51,7 @@ namespace parser PResult parseStmt(State& st, bool allowExprs) { if(!st.hasTokens()) - unexpected(st, "end of file"); + return PResult::insufficientTokensError(); st.skipWS(); @@ -274,12 +274,12 @@ namespace parser }(); return ret.mutate([&](auto ret) { - if(!st.isInFunctionBody() && !st.isInStructBody()) + if(!allowExprs && !st.isInFunctionBody() && !st.isInStructBody()) error(ret, "%s is not allowed at the top-level", ret->readableName); }); } - unexpected(st.loc(), "end of file"); + return PResult::insufficientTokensError(); } @@ -1074,7 +1074,7 @@ namespace parser if(raw) error(st.loc(), "initialisation body cannot be used with raw array allocations"); // ok, get it - ret->initBody = parseBracedBlock(st); + ret->initBody = parseBracedBlock(st).val(); } @@ -1098,7 +1098,7 @@ namespace parser auto ret = util::pool(st.eat().loc); if(st.front() == TT::LBrace) - ret->actual = parseBracedBlock(st); + ret->actual = parseBracedBlock(st).val(); else ret->actual = parseStmt(st).val(); diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index 433a63f9..99a74a93 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -151,7 +151,7 @@ namespace parser st.enterFunctionBody(); { - defn->body = parseBracedBlock(st); + defn->body = parseBracedBlock(st).val(); } st.leaveFunctionBody(); @@ -247,7 +247,7 @@ namespace parser st.enterFunctionBody(); { - ret->body = parseBracedBlock(st); + ret->body = parseBracedBlock(st).val(); } st.leaveFunctionBody(); @@ -277,7 +277,7 @@ namespace parser st.enterFunctionBody(); { - ret->body = parseBracedBlock(st); + ret->body = parseBracedBlock(st).val(); } st.leaveFunctionBody(); @@ -295,7 +295,7 @@ namespace parser st.enterFunctionBody(); { - ret->body = parseBracedBlock(st); + ret->body = parseBracedBlock(st).val(); } st.leaveFunctionBody(); @@ -380,7 +380,7 @@ namespace parser - Block* parseBracedBlock(State& st) + PResult parseBracedBlock(State& st) { st.skipWS(); @@ -391,24 +391,31 @@ namespace parser st.skipWS(); while(st.front() != TT::RBrace) { - auto stmt = parseStmt(st).val(); - if(auto defer = dcast(DeferredStmt, stmt)) - ret->deferredStatements.push_back(defer); + if(!st.hasTokens()) + return PResult::insufficientTokensError(); - else - ret->statements.push_back(stmt); + auto s = parseStmt(st).mutate([&](auto stmt) { + if(auto defer = dcast(DeferredStmt, stmt)) + ret->deferredStatements.push_back(defer); + else + ret->statements.push_back(stmt); - if(st.front() == TT::NewLine || st.front() == TT::Comment || st.front() == TT::Semicolon) - st.pop(); - else if(st.frontAfterWS() == TT::RBrace) - break; + if(st.front() == TT::NewLine || st.front() == TT::Comment || st.front() == TT::Semicolon) + st.pop(); - else - expected(st, "newline or semicolon to terminate a statement", st.front().str()); + else if(st.frontAfterWS() == TT::RBrace) + return; - st.skipWS(); + else + expected(st, "newline or semicolon to terminate a statement", st.front().str()); + + st.skipWS(); + }); + + if(s.isError()) + return PResult::copyError(s); } auto closing = st.eat(); diff --git a/source/frontend/parser/misc.cpp b/source/frontend/parser/misc.cpp index e210a121..bf793343 100644 --- a/source/frontend/parser/misc.cpp +++ b/source/frontend/parser/misc.cpp @@ -92,7 +92,7 @@ namespace parser st.enterFunctionBody(); defer(st.leaveFunctionBody()); - if(st.front() == TT::LBrace) ret->block = parseBracedBlock(st); + if(st.front() == TT::LBrace) ret->block = parseBracedBlock(st).val(); else ret->insideExpr = parseExpr(st); return ret; diff --git a/source/frontend/parser/operators.cpp b/source/frontend/parser/operators.cpp index 620c98ef..36943a49 100644 --- a/source/frontend/parser/operators.cpp +++ b/source/frontend/parser/operators.cpp @@ -55,7 +55,7 @@ namespace parser st.enterFunctionBody(); - ret->body = parseBracedBlock(st); + ret->body = parseBracedBlock(st).val(); ret->name = ret->symbol; st.leaveFunctionBody(); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index 890db249..d51e79b9 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -77,7 +77,7 @@ namespace parser st.enterStructBody(); - auto blk = parseBracedBlock(st); + auto blk = parseBracedBlock(st).val(); for(auto s : blk->statements) { if(auto v = dcast(VarDefn, s)) diff --git a/source/include/defs.h b/source/include/defs.h index 5e068dc0..3c2ad42f 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -56,10 +56,19 @@ template [[noreturn]] inline void _error_and_exit(const char* fmt, Ts&&... ts) { // tinyformat::format(std::cerr, fmt, ts...); - fprintf(stderr, "%s", zpr::sprint(fmt, ts...).c_str()); + fprintf(stderr, "%s\n", zpr::sprint(fmt, ts...).c_str()); doTheExit(); } +namespace platform { void printStackTrace(); } +template +[[noreturn]] inline void compiler_crash(const char* fmt, Ts&&... ts) +{ + fprintf(stderr, "%s\n", zpr::sprint(fmt, ts...).c_str()); + + platform::printStackTrace(); + abort(); +} template diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 1d7a3146..44cb21ca 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -78,8 +78,11 @@ namespace parser void skipWS() { - while(this->tokens[this->index] == lexer::TokenType::NewLine || this->tokens[this->index] == lexer::TokenType::Comment) + while(this->index < this->tokens.size() && (this->tokens[this->index] == lexer::TokenType::NewLine + || this->tokens[this->index] == lexer::TokenType::Comment)) + { this->index++; + } } const lexer::Token& front() const @@ -138,7 +141,7 @@ namespace parser bool hasTokens() const { - return this->index < this->tokens.size(); + return this->index < this->tokens.size() && this->tokens[this->index] != lexer::TokenType::EndOfFile; } const lexer::TokenList& getTokenList() @@ -267,6 +270,14 @@ namespace parser this->state = (needsmore ? 2 : 1); } + private: + explicit PResult(ErrorMsg* err, int stt) + { + this->error = err; + this->state = stt; + } + public: + PResult(const PResult& r) { @@ -310,7 +321,7 @@ namespace parser operator A() const { if(this->state == 0) return PResult(this->value); - else return PResult(this->error); + else return PResult(this->error, this->state); } @@ -339,13 +350,13 @@ namespace parser ErrorMsg* err() const { - if(this->state < 1) _error_and_exit("not error\n"); + if(this->state < 1) compiler_crash("not error"); else return this->error; } T* val() const { - if(this->state != 0) _error_and_exit("no value\n"); + if(this->state != 0) compiler_crash("no value"); else return this->value; } @@ -359,6 +370,28 @@ namespace parser return this->state == 0; } + bool needsMoreTokens() const + { + return this->state == 2; + } + + static PResult insufficientTokensError() + { + return PResult(BareError::make("unexpected end of input"), /* needmore: */ true); + } + + template + static PResult copyError(const PResult& other) + { + // only for error. + if(other.state < 1) + compiler_crash("not error"); + + return PResult(other.err(), other.state); + } + + template + friend struct PResult; using value_t = T; @@ -440,7 +473,7 @@ namespace parser ast::DeallocOp* parseDealloc(State& st); ast::SizeofOp* parseSizeof(State& st); - ast::Block* parseBracedBlock(State& st); + PResult parseBracedBlock(State& st); ast::LitNumber* parseNumber(State& st); ast::LitString* parseString(State& st, bool israw); @@ -448,7 +481,7 @@ namespace parser ast::Stmt* parseForLoop(State& st); ast::Stmt* parseIfStmt(State& st); - ast::WhileLoop* parseWhileLoop(State& st); + PResult parseWhileLoop(State& st); ast::TopLevelBlock* parseTopLevel(State& st, const std::string& name); diff --git a/source/include/repl.h b/source/include/repl.h index e14bd4d7..fe60391f 100644 --- a/source/include/repl.h +++ b/source/include/repl.h @@ -8,7 +8,8 @@ namespace repl { void start(); - void processLine(const std::string& line); + void setupEnvironment(); + bool processLine(const std::string& line); template diff --git a/source/platform/backtrace.cpp b/source/platform/backtrace.cpp index c10070a5..e0e7597f 100644 --- a/source/platform/backtrace.cpp +++ b/source/platform/backtrace.cpp @@ -23,7 +23,8 @@ extern "C" { namespace platform { - constexpr size_t MAX_FRAMES = 128; + constexpr size_t MAX_FRAMES = 128; + constexpr size_t SKIP_FRAMES = 1; struct piece_t { @@ -49,7 +50,7 @@ namespace platform std::vector pieces; - for(size_t i = 5; i < num; i++) + for(size_t i = SKIP_FRAMES; i < num; i++) { // platform-specific output! if constexpr (OS_DARWIN) diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 61ac2e39..0c321d9e 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -21,27 +21,44 @@ namespace repl void start() { + setupEnvironment(); + linenoiseSetMultiLine(1); + printf("flax repl -- version %s\n", frontend::getVersion().c_str()); printf("type :help for help\n\n"); - linenoiseSetMultiLine(1); - while(char* _line = linenoise(PROMPT_STRING)) + std::string input; + while(char* line = linenoise(PROMPT_STRING)) { - std::string line = _line; - linenoiseFree(_line); + input += std::string(line) + "\n"; + linenoiseFree(line); - if(line.empty()) + if(input.empty()) continue; - if(line[0] == ':') + if(input[0] == ':') { - runCommand(line.substr(1)); + runCommand(input.substr(1)); printf("\n"); continue; } - processLine(line); - printf("\n"); + if(bool needmore = processLine(input); needmore) + { + // read more. + while(needmore) + { + char* line = linenoise(CONTINUATION_PROMPT_STRING); + + input += std::string(line) + "\n"; + linenoiseFree(line); + + needmore = processLine(input); + } + } + + // ok, we're done -- clear. + input.clear(); } } } diff --git a/source/repl/parse.cpp b/source/repl/parse.cpp index 0d3e260c..3b6477c6 100644 --- a/source/repl/parse.cpp +++ b/source/repl/parse.cpp @@ -5,10 +5,29 @@ #include "repl.h" #include "parser.h" #include "frontend.h" +#include "parser_internal.h" + +#include "memorypool.h" namespace repl { - void processLine(const std::string& line) + struct State + { + State() + { + this->topLevelAst = util::pool(Location(), "__repl"); + } + + ast::TopLevelBlock* topLevelAst; + }; + + static State* state = 0; + void setupEnvironment() + { + state = new State(); + } + + bool processLine(const std::string& line) { std::string replName = ""; @@ -18,7 +37,38 @@ namespace repl platform::cachePreExistingFile(replName, line); auto lexResult = frontend::lexTokensFromString(replName, line); - // parse - auto parseResult = parser::parseFile(replName, lexResult, collector); + // parse, but first setup the environment. + auto st = parser::State(lexResult.tokens); + auto stmt = parser::parseStmt(st, /* exprs: */ true); + + if(stmt.needsMoreTokens()) + { + return true; + } + else if(stmt.isError()) + { + stmt.err()->post(); + } + else + { + state->topLevelAst->statements.push_back(stmt.val()); + } + + return false; } } + + + + + + + + + + + + + + + From c83f12d272d6a3021a9900506269470098d4db52 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 6 Nov 2019 21:52:38 +0800 Subject: [PATCH 055/129] hmmmm --- source/include/ztmu.h | 807 +++++++++++++++++++++++++++++++++++++++++ source/repl/driver.cpp | 88 +++-- 2 files changed, 859 insertions(+), 36 deletions(-) create mode 100644 source/include/ztmu.h diff --git a/source/include/ztmu.h b/source/include/ztmu.h new file mode 100644 index 00000000..d7986a42 --- /dev/null +++ b/source/include/ztmu.h @@ -0,0 +1,807 @@ +// ztmu.h +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +// terminal manipulation utilities + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + + +namespace ztmu +{ + struct State + { + void clear(); + + std::string read(); + std::string readContinuation(); + + void setPrompt(const std::string& prompt); + void setContinuationPrompt(const std::string& prompt); + + + + + bool isContinuationMode = false; + + std::string promptString; + std::string contPromptString; + + size_t cursor = 0; + size_t byteCursor = 0; + std::string currLine; + + std::vector lines; + }; +} + + + + + +namespace ztmu { +namespace detail +{ + struct CursorPosition + { + int x = -1; + int y = -1; + }; + + enum class Key + { + NONE = 0, + ESCAPE, + UP, + DOWN, + LEFT, + RIGHT, + HOME, + END, + DELETE, + INSERT, + PAGEUP, + PAGEDOWN, + F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, + F10, F11, F12, F13, F14, F15, F16, F17, + F18, F19, F20, F21, F22, F23, F24 + }; + + struct ControlSeq + { + ControlSeq() { } + ControlSeq(char c) : lastChar(c) { } + + char lastChar; + + // only support max 8 params for now. + int numParams = 0; + int params[8]; + }; + + struct KeyEvent + { + union { + char key; + Key escKey; + ControlSeq ctrlSeq; + }; + + KeyEvent() + { + this->escKey = Key::NONE; + this->isNormalChar = false; + this->isControlSeq = false; + } + + KeyEvent(const ControlSeq& cs) + { + this->ctrlSeq = cs; + this->isNormalChar = false; + this->isControlSeq = true; + } + + KeyEvent(Key escKey) + { + this->escKey = escKey; + this->isNormalChar = false; + this->isControlSeq = false; + } + + KeyEvent(char c) + { + this->key = c; + this->isNormalChar = true; + this->isControlSeq = false; + } + + KeyEvent ctrl() + { + auto copy = *this; + copy.isControlled = true; + return copy; + } + + KeyEvent alt() + { + auto copy = *this; + copy.isAlted = true; + return copy; + } + + KeyEvent shift() + { + auto copy = *this; + copy.isShifted = true; + return copy; + } + + + bool isNormalChar = true; + bool isControlSeq = false; + + bool isAlted = false; + bool isShifted = false; + bool isControlled = false; + }; + + + static void leaveRawMode(); + static void enterRawMode(); + + static int platform_read_one(char* c); + static int platform_read(char* c, size_t len); + static void platform_write(const std::string& s); + static void platform_write(const char* s, size_t len); + + static std::string moveCursorUp(int n); + static std::string moveCursorDown(int n); + static std::string moveCursorLeft(int n); + static std::string moveCursorRight(int n); + static CursorPosition getCursorPosition(); + static std::string setCursorPosition(const CursorPosition& pos); + + static size_t displayedTextLength(const std::string_view& str); + static std::pair parseEscapeSequence(const std::string_view& str); + + + + + + + + + + + + + + + + + + + + + +#if ZTMU_CREATE_IMPL || true + +// comes as a pair yo +#include "zpr.h" + +#ifndef _WIN32 + + #include + #include + + static struct termios original_termios; + + + static inline void platform_write(const char* s, size_t len) + { + write(STDOUT_FILENO, s, len); + } + + static inline int platform_read_one(char* c) + { + return read(STDIN_FILENO, c, 1); + } + + static inline int platform_read(char* c, size_t len) + { + return read(STDIN_FILENO, c, len); + } + +#else + #include + + static inline void platform_write(const char* s, size_t len) + { + _write(STDOUT_FILENO, s, len); + } + + static inline int platform_read_one(char* c) + { + return _read(STDIN_FILENO, c, 1); + } + + static inline int platform_read(char* c, size_t len) + { + return _read(STDIN_FILENO, c, len); + } + +#endif + + + + + + + static constexpr const char ESC = '\x1b'; + static constexpr const char* CSI = "\x1b["; + + static bool didRegisterAtexit = false; + static bool isInRawMode = false; + + static inline void platform_write(const std::string& s) + { + platform_write(s.c_str(), s.size()); + } + + static inline std::string moveCursorUp(int n) + { + if(n < 0) return moveCursorDown(-n); + else return zpr::sprint("%s%dA", CSI, n); + } + + static inline std::string moveCursorDown(int n) + { + if(n < 0) return moveCursorUp(-n); + else return zpr::sprint("%s%dB", CSI, n); + } + + static inline std::string moveCursorLeft(int n) + { + if(n < 0) return moveCursorRight(-n); + else return zpr::sprint("%s%dD", CSI, n); + } + + static inline std::string moveCursorRight(int n) + { + if(n < 0) return moveCursorLeft(-n); + else return zpr::sprint("%s%dC", CSI, n); + } + + static CursorPosition getCursorPosition() + { + platform_write(zpr::sprint("%s6n", CSI)); + + // ok, time to read it back. format is CSI [ rows ; cols R + char buf[33] { 0 }; + for(size_t i = 0; i < 32; i++) + { + if(read(STDIN_FILENO, buf + i, 1) != 1) + break; + + if(buf[i] == 'R') + break; + } + + // give up. + if(buf[0] != ESC || buf[1] != '[') + return CursorPosition(); + + CursorPosition ret; + int gotted = sscanf(&buf[2], "%d;%d", &ret.y, &ret.x); + if(gotted != 2) + return CursorPosition(); + + return ret; + } + + static std::string setCursorPosition(const CursorPosition& pos) + { + if(pos.x <= 0 || pos.y <= 0) + return ""; + + return zpr::sprint("%s%d;%dH", CSI, pos.y, pos.x); + } + + + + + + + + + + + + static std::pair parseEscapeSequence(const std::string_view& str) + { + if(str.size() < 2 || str[0] != ESC) + return std::make_pair(KeyEvent(), 0); + + auto read_digits = [](const std::string_view& s, int* out) -> size_t { + int ret = 0; + size_t cons = 0; + for(char c : s) + { + if(!isdigit(c)) + break; + + cons++; + ret = (10 * ret) + (c - '0'); + } + + *out = ret; + return cons; + }; + + + + + if(str[1] == ESC) + { + return std::make_pair(KeyEvent(Key::ESCAPE), 2); + } + else if(str[1] == 'N' || str[1] == 'O' || str[1] == 'c') + { + return std::make_pair(KeyEvent(ControlSeq(str[1])), 2); + } + else if(str[1] == 'P' || str[1] == ']' || str[1] == 'X' || str[1] == '^' || str[1] == '_') + { + // read till the terminator (ESC\). + size_t cons = 2; + while(cons + 1 < str.size() && (str[cons] != ESC || str[cons + 1] != '\\')) + cons++; + + return std::make_pair(ControlSeq(str[1]), cons); + } + else if(str[1] == '[') + { + // parse the params. + ControlSeq csi; + + size_t cons = 2; + while(csi.numParams < 8) + { + int n = 0; + size_t rd = read_digits(str.substr(cons), &n); + if(rd > 0) + { + csi.params[csi.numParams++] = n; + cons += rd; + + if(cons < str.size() && str[cons] == ';') + { + cons++; + continue; + } + else + { + break; + } + } + else + { + break; + } + } + + csi.lastChar = str[cons++]; + switch(csi.lastChar) + { + case '~': { + auto ke = KeyEvent(); + switch(csi.params[0]) + { + case 1: ke = KeyEvent(Key::HOME); break; + case 2: ke = KeyEvent(Key::INSERT); break; + case 3: ke = KeyEvent(Key::DELETE); break; + case 4: ke = KeyEvent(Key::END); break; + case 5: ke = KeyEvent(Key::PAGEUP); break; + case 6: ke = KeyEvent(Key::PAGEDOWN); break; + case 7: ke = KeyEvent(Key::HOME); break; + case 8: ke = KeyEvent(Key::END); break; + case 10: ke = KeyEvent(Key::F0); break; + case 11: ke = KeyEvent(Key::F1); break; + case 12: ke = KeyEvent(Key::F2); break; + case 13: ke = KeyEvent(Key::F3); break; + case 14: ke = KeyEvent(Key::F4); break; + case 15: ke = KeyEvent(Key::F5); break; + case 17: ke = KeyEvent(Key::F6); break; + case 18: ke = KeyEvent(Key::F7); break; + case 19: ke = KeyEvent(Key::F8); break; + case 20: ke = KeyEvent(Key::F9); break; + case 21: ke = KeyEvent(Key::F10); break; + case 23: ke = KeyEvent(Key::F11); break; + case 24: ke = KeyEvent(Key::F12); break; + case 25: ke = KeyEvent(Key::F13); break; + case 26: ke = KeyEvent(Key::F14); break; + case 28: ke = KeyEvent(Key::F15); break; + case 29: ke = KeyEvent(Key::F16); break; + case 31: ke = KeyEvent(Key::F17); break; + case 32: ke = KeyEvent(Key::F18); break; + case 33: ke = KeyEvent(Key::F19); break; + case 34: ke = KeyEvent(Key::F20); break; + } + + if(ke.escKey != Key::NONE && csi.numParams == 2) + { + int mods = csi.params[1] - 1; + if(mods & 0x1) ke = ke.shift(); + if(mods & 0x2) ke = ke.alt(); + if(mods & 0x4) ke = ke.ctrl(); + if(mods & 0x8) ke = ke.alt(); // actually meta + } + + return std::make_pair(ke, cons); + } + + case 'A': return std::make_pair(KeyEvent(Key::UP), cons); + case 'B': return std::make_pair(KeyEvent(Key::DOWN), cons); + case 'C': return std::make_pair(KeyEvent(Key::RIGHT), cons); + case 'D': return std::make_pair(KeyEvent(Key::LEFT), cons); + case 'F': return std::make_pair(KeyEvent(Key::END), cons); + case 'H': return std::make_pair(KeyEvent(Key::HOME), cons); + + case 'P': return std::make_pair(KeyEvent(Key::F1), cons); + case 'Q': return std::make_pair(KeyEvent(Key::F2), cons); + case 'R': return std::make_pair(KeyEvent(Key::F3), cons); + case 'S': return std::make_pair(KeyEvent(Key::F4), cons); + } + + + + return std::make_pair(KeyEvent(csi), cons); + } + else + { + return std::make_pair(KeyEvent(), 0); + } + } + + + static size_t displayedTextLength(const std::string_view& str) + { + auto utf8_len = [](const char* begin, const char* end) -> size_t { + size_t len = 0; + + while(*begin && begin != end) + { + if((*begin & 0xC0) != 0x80) + len += 1; + + begin++; + } + return len; + }; + + auto len = utf8_len(str.begin(), str.end()); + { + size_t cons = 0; + std::string_view sv = str; + + size_t i = 0; + while(i < sv.size()) + { + if(sv[i] == ESC) + { + size_t k = parseEscapeSequence(sv.substr(i)).second; + i += k; + cons += k; + } + else + { + i++; + } + } + + return len - cons; + } + } + + static void leaveRawMode() + { + if(isInRawMode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios) != -1) + isInRawMode = false; + } + + static void enterRawMode() + { + if(!isatty(STDIN_FILENO)) + return; + + if(!didRegisterAtexit) + { + atexit([]() { + leaveRawMode(); + }); + + didRegisterAtexit = true; + } + + if(tcgetattr(STDIN_FILENO, &original_termios) == -1) + return; + + // copy the original + termios raw = original_termios; + + // input modes: no break, no CR to NL, no parity check, no strip char, + // no start/stop output control. + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + + // output modes - disable post processing + raw.c_oflag &= ~(OPOST); + + // control modes - set 8 bit chars + raw.c_cflag |= (CS8); + + // local modes - choing off, canonical off, no extended functions, + // no signal chars (^Z,^C) + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + + // control chars - set return condition: min number of bytes and timer. + // We want read to return every single byte, without timeout. + raw.c_cc[VMIN] = 1; + raw.c_cc[VTIME] = 0; + + /* put terminal in raw mode after flushing */ + if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) + return; + + isInRawMode = true; + } + + + + + static void refresh_line(State* st) + { + // let's not care about multiline for now. + + // kill the entire line, and move the cursor to the beginning as well. + platform_write("\x1b[2K"); + platform_write(moveCursorLeft(9999)); + + // print out the prompt. + auto pr = st->isContinuationMode ? st->contPromptString : st->promptString; + auto promptLen = displayedTextLength(pr); + + platform_write(pr); + platform_write(st->currLine); + + // move the cursor to the right by the cursor. + platform_write(moveCursorLeft(9999)); + platform_write(moveCursorRight(st->cursor + promptLen)); + } + + static size_t findBeginningOfUTF8CP(const uint8_t* bytes, size_t i) + { + if(bytes[i] <= 0x7F) + return i; + + while(i > 0 && (bytes[i] & 0xC0) == 0x80) + i -= 1; + + return i; + } + + static bool read_line(State* st) + { + constexpr char CTRL_C = '\x03'; + constexpr char CTRL_D = '\x04'; + constexpr char BACKSPACE_ = '\x08'; + constexpr char ENTER = '\x0D'; + constexpr char BACKSPACE = '\x7F'; + + enterRawMode(); + + bool eof = false; + while(true) + { + char c = 0; + if(platform_read_one(&c) <= 0) + break; + + // fprintf(stderr, "[%d]", c); + switch(c) + { + // enter + case ENTER: { + goto finish; + } + + // this is a little complex; if there's stuff in the buffer, then don't do anything + // possibly do a bell -- swift and python both do this. + case CTRL_D: { + + // if the buffer is empty, then quit. + if(st->currLine.empty()) + { + eof = true; + goto finish; + } + } break; + + case CTRL_C: { + if(st->currLine.empty()) + { + eof = true; + goto finish; + } + else + { + st->currLine.clear(); + st->cursor = 0; + refresh_line(st); + } + } + + // backspace + case BACKSPACE: [[fallthrough]]; + case BACKSPACE_: { + if(st->cursor > 0 && st->currLine.size() > 0) + { + auto x = st->byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + + st->currLine.erase(l, x - l + 1); + st->cursor -= 1; + st->byteCursor -= (x - l + 1); + } + + refresh_line(st); + } break; + + // time for some fun. + case ESC: { + + } break; + + // default: just append -- if it's not a control char. + default: { + uint8_t uc = static_cast(c); + if(uc >= 0x20 && uc <= 0x7F) + { + st->currLine += c; + + st->cursor += 1; + st->byteCursor += 1; + } + else if(uc >= 0x20) + { + std::string cp; + cp += c; + + // if it's unicode, then try and read even more. + // see: https://en.wikipedia.org/wiki/UTF-8 for the bitmasks we're using. + if((uc & 0xE0) == 0xC0) + { + // one extra byte + char c = 0; platform_read_one(&c); + cp += c; + } + else if((uc & 0xF0) == 0xE0) + { + // two extra bytes + char buf[2]; platform_read(buf, 2); + cp += buf; + } + else if((uc & 0xF8) == 0xF0) + { + // three extra bytes + char buf[3]; platform_read(buf, 3); + cp += buf; + } + + st->currLine += cp; + st->byteCursor += cp.size(); + st->cursor += displayedTextLength(cp); + } + + refresh_line(st); + } break; + } + } + + finish: + leaveRawMode(); + return eof; + } + + + + +#endif + +} +} + + +namespace ztmu +{ + void State::clear() + { + // reset the thing. + this->lines.clear(); + } + + void State::setPrompt(const std::string& prompt) + { + this->promptString = prompt; + } + + void State::setContinuationPrompt(const std::string& prompt) + { + this->contPromptString = prompt; + } + + std::string State::read() + { + // clear. + this->clear(); + + detail::platform_write(this->promptString); + bool eof = detail::read_line(this); + + if(!eof) + { + this->lines.push_back(this->currLine); + this->currLine.clear(); + + return this->lines.back(); + } + else + { + return "\x4"; + } + } + + std::string State::readContinuation() + { + // don't clear. + detail::platform_write(this->contPromptString); + bool eof = detail::read_line(this); + + if(!eof) + { + this->lines.push_back(this->currLine); + this->currLine.clear(); + + return this->lines.back(); + } + else + { + return "\x4"; + } + } +} + + + + + + + + + + + + + + + + + + diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 0c321d9e..e8b594d6 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -2,10 +2,13 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. +#include + #include "repl.h" #include "frontend.h" -#include "linenoise/linenoise.h" +#define ZTMU_CREATE_IMPL 1 +#include "ztmu.h" namespace repl { @@ -21,48 +24,61 @@ namespace repl void start() { - setupEnvironment(); - linenoiseSetMultiLine(1); - printf("flax repl -- version %s\n", frontend::getVersion().c_str()); printf("type :help for help\n\n"); + setupEnvironment(); + std::string input; - while(char* line = linenoise(PROMPT_STRING)) - { - input += std::string(line) + "\n"; - linenoiseFree(line); - - if(input.empty()) - continue; - - if(input[0] == ':') - { - runCommand(input.substr(1)); - printf("\n"); - continue; - } - - if(bool needmore = processLine(input); needmore) - { - // read more. - while(needmore) - { - char* line = linenoise(CONTINUATION_PROMPT_STRING); - - input += std::string(line) + "\n"; - linenoiseFree(line); - - needmore = processLine(input); - } - } - - // ok, we're done -- clear. - input.clear(); - } + + auto st = ztmu::State(); + st.setPrompt(PROMPT_STRING); + st.setContinuationPrompt(CONTINUATION_PROMPT_STRING); + + printf("\nread: %s\n", st.read().c_str()); + + + + + + // printf("len = %zu\n", ztmu::detail::displayedTextLength(PROMPT_STRING)); + + + + + // while(auto line = (PROMPT_STRING)) + // { + // input += std::string(line); + + // if(input.empty()) + // continue; + + // if(input[0] == ':') + // { + // runCommand(input.substr(1)); + // printf("\n"); + // continue; + // } + + // if(bool needmore = processLine(input + "\n"); needmore) + // { + // // read more. + // while(needmore) + // { + // auto line = (CONTINUATION_PROMPT_STRING); + // input += std::string(line) + "\n"; + + // needmore = processLine(input); + // } + // } + + // // ok, we're done -- clear. + // input.clear(); + // } } } +// std::wcerr << L""; From 405773afbb713f98c2566b87793916f9b795d5b2 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 6 Nov 2019 22:27:30 +0800 Subject: [PATCH 056/129] basic line editing --- source/include/ztmu.h | 153 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 13 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index d7986a42..60a5f19b 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -597,6 +597,24 @@ namespace detail return i; } + static size_t findEndOfUTF8CP(const uint8_t* bytes, size_t i) + { + if(bytes[i] <= 0x7F) + return i; + + if((bytes[i] & 0xE0) == 0xC0) + return i + 1; + + else if((bytes[i] & 0xF0) == 0xE0) + return i + 2; + + else if((bytes[i] & 0xF8) == 0xF0) + return i + 3; + + return i; + } + + static bool read_line(State* st) { constexpr char CTRL_C = '\x03'; @@ -607,6 +625,80 @@ namespace detail enterRawMode(); + auto cursor_home = [&st]() { + st->cursor = 0; + st->byteCursor = 0; + + refresh_line(st); + }; + + auto cursor_end = [&st]() { + st->cursor = displayedTextLength(st->currLine); + st->byteCursor = st->currLine.size(); + + refresh_line(st); + }; + + auto cursor_left = [&st]() { + if(st->cursor > 0) + { + auto x = st->byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + st->byteCursor -= (x - l + 1); + st->cursor -= 1; + + refresh_line(st); + } + }; + + auto cursor_right = [&st]() { + if(st->byteCursor < st->currLine.size()) + { + auto x = st->byteCursor; + auto l = findEndOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + + st->byteCursor += (l - x + 1); + st->cursor += 1; + + refresh_line(st); + } + }; + + + + auto delete_left = [&st]() { + if(st->cursor > 0 && st->currLine.size() > 0) + { + auto x = st->byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + + st->currLine.erase(l, x - l + 1); + st->byteCursor -= (x - l + 1); + st->cursor -= 1; + + refresh_line(st); + } + }; + + auto delete_right = [&st]() { + if(st->byteCursor < st->currLine.size()) + { + auto x = st->byteCursor; + auto l = findEndOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + + st->currLine.erase(x, l - x + 1); + + // both cursors remain unchanged. + refresh_line(st); + } + }; + + + + + + + bool eof = false; while(true) { @@ -651,21 +743,55 @@ namespace detail // backspace case BACKSPACE: [[fallthrough]]; case BACKSPACE_: { - if(st->cursor > 0 && st->currLine.size() > 0) - { - auto x = st->byteCursor - 1; - auto l = findBeginningOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); - - st->currLine.erase(l, x - l + 1); - st->cursor -= 1; - st->byteCursor -= (x - l + 1); - } - - refresh_line(st); + delete_left(); } break; // time for some fun. case ESC: { + // there should be at least two more! + char seq[2]; platform_read(seq, 2); + + if(seq[0] == '[') + { + if(seq[1] >= '0' && seq[1] <= '9') + { + int n = 0; + + // ok now time to read until a ~ + char x = seq[1]; + while(x != '~') + { + n = (10 * n) + (x - '0'); + platform_read_one(&x); + } + + switch(n) + { + case 3: delete_right(); break; + + case 1: cursor_home(); break; + case 7: cursor_home(); break; + + case 4: cursor_end(); break; + case 8: cursor_end(); break; + } + } + else + { + switch(seq[1]) + { + case 'C': cursor_right(); break; + case 'D': cursor_left(); break; + case 'H': cursor_home(); break; + case 'F': cursor_end(); break; + } + } + } + else if(seq[0] == 'O') + { + if(seq[1] == 'H') cursor_home(); + else if(seq[1] == 'F') cursor_end(); + } } break; @@ -674,7 +800,7 @@ namespace detail uint8_t uc = static_cast(c); if(uc >= 0x20 && uc <= 0x7F) { - st->currLine += c; + st->currLine.insert(st->currLine.begin() + st->byteCursor, c); st->cursor += 1; st->byteCursor += 1; @@ -705,7 +831,8 @@ namespace detail cp += buf; } - st->currLine += cp; + st->currLine.insert(st->byteCursor, cp); + st->byteCursor += cp.size(); st->cursor += displayedTextLength(cp); } From 8e662dd11d3b87b5a097a937b288743848ad7ddf Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 7 Nov 2019 11:25:56 +0800 Subject: [PATCH 057/129] rudimentary multi-line support --- source/include/ztmu.h | 424 +++++++++++++++++++++++++++++++++++------ source/repl/driver.cpp | 6 +- 2 files changed, 369 insertions(+), 61 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 60a5f19b..db9cfae2 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -26,19 +26,26 @@ namespace ztmu std::string readContinuation(); void setPrompt(const std::string& prompt); + void setWrappedPrompt(const std::string& prompt); void setContinuationPrompt(const std::string& prompt); - - bool isContinuationMode = false; + // 0 = normal, 1 = continuation + int promptMode = 0; std::string promptString; std::string contPromptString; + std::string wrappedPromptString; size_t cursor = 0; size_t byteCursor = 0; - std::string currLine; + + size_t lineIdx = 0; + size_t wrappedLineIdx = 0; + + size_t termWidth = 0; + size_t termHeight = 0; std::vector lines; }; @@ -160,8 +167,8 @@ namespace detail static int platform_read_one(char* c); static int platform_read(char* c, size_t len); - static void platform_write(const std::string& s); static void platform_write(const char* s, size_t len); + static void platform_write(const std::string_view& sv); static std::string moveCursorUp(int n); static std::string moveCursorDown(int n); @@ -201,7 +208,9 @@ namespace detail #ifndef _WIN32 #include + #include #include + #include static struct termios original_termios; @@ -252,31 +261,35 @@ namespace detail static bool didRegisterAtexit = false; static bool isInRawMode = false; - static inline void platform_write(const std::string& s) + static inline void platform_write(const std::string_view& sv) { - platform_write(s.c_str(), s.size()); + platform_write(sv.data(), sv.size()); } static inline std::string moveCursorUp(int n) { + if(n == 0) return ""; if(n < 0) return moveCursorDown(-n); else return zpr::sprint("%s%dA", CSI, n); } static inline std::string moveCursorDown(int n) { + if(n == 0) return ""; if(n < 0) return moveCursorUp(-n); else return zpr::sprint("%s%dB", CSI, n); } static inline std::string moveCursorLeft(int n) { + if(n == 0) return ""; if(n < 0) return moveCursorRight(-n); else return zpr::sprint("%s%dD", CSI, n); } static inline std::string moveCursorRight(int n) { + if(n == 0) return ""; if(n < 0) return moveCursorLeft(-n); else return zpr::sprint("%s%dC", CSI, n); } @@ -563,27 +576,61 @@ namespace detail isInRawMode = true; } - - - - static void refresh_line(State* st) + static size_t getTerminalWidth() { - // let's not care about multiline for now. - - // kill the entire line, and move the cursor to the beginning as well. - platform_write("\x1b[2K"); - platform_write(moveCursorLeft(9999)); - - // print out the prompt. - auto pr = st->isContinuationMode ? st->contPromptString : st->promptString; - auto promptLen = displayedTextLength(pr); + #ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + return csbi.srWindow.Right - csbi.srWindow.Left + 1; + #else + + #ifdef _MSC_VER + #else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + return w.ws_col; + + #ifdef _MSC_VER + #else + #pragma GCC diagnostic pop + #endif + + #endif + } - platform_write(pr); - platform_write(st->currLine); + static size_t getTerminalHeight() + { + #ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + #else + + #ifdef _MSC_VER + #else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + return w.ws_row; + + #ifdef _MSC_VER + #else + #pragma GCC diagnostic pop + #endif + + #endif + } - // move the cursor to the right by the cursor. - platform_write(moveCursorLeft(9999)); - platform_write(moveCursorRight(st->cursor + promptLen)); + static std::string& getCurLine(State* st) + { + return st->lines[st->lineIdx]; } static size_t findBeginningOfUTF8CP(const uint8_t* bytes, size_t i) @@ -615,6 +662,218 @@ namespace detail } + + static void refresh_line(State* st, std::string* oldLine = 0) + { + // what we want to do is write the entire command + text string at once to reduce flickering. + // for now, meh. + + auto pr = st->promptMode == 0 ? st->promptString : st->contPromptString; + auto promptLen = displayedTextLength(pr); + + auto wrapPL = displayedTextLength(st->wrappedPromptString); + + if(!oldLine) + oldLine = &getCurLine(st); + + std::string commands; + auto qcmd = [&commands](const std::string_view& c) { + commands += c; + }; + + auto flushcmd = [&commands]() { + platform_write(commands); + commands.clear(); + }; + + + // kill the entire line, and move the cursor to the beginning as well. + qcmd("\x1b[2K"); + qcmd(moveCursorLeft(9999)); + + auto calc_wrap = [&](size_t len) -> size_t { + auto width = st->termWidth; + auto norm_pl = promptLen; + auto wrap_pl = displayedTextLength(st->wrappedPromptString); + + size_t lines = 1; + while(len > 0) + { + auto left = width - (lines == 1 ? norm_pl : wrap_pl); + if(left == len) + lines += 1; + + len -= std::min(left, len); + if(len == 0) + break; + + lines += 1; + } + + return lines; + }; + + auto calc_wli = [&](size_t len) { + size_t done = 0; + size_t remaining = len; + + size_t lc = 1; + st->wrappedLineIdx = 0; + + while(true) + { + auto left = st->termWidth - (lc == 1 ? promptLen : wrapPL); + auto todo = std::min(left, remaining); + + if(remaining == left) + st->wrappedLineIdx++; + + remaining -= todo; + done += todo; + + if(remaining == 0) + break; + + // if your cursor is beyond this point, then you are on the next line. + if(st->cursor >= done + 1) + { + st->wrappedLineIdx++; + lc++; + } + } + }; + + + std::string_view currentLine = getCurLine(st); + + auto old_strlen = displayedTextLength(*oldLine); + auto new_strlen = displayedTextLength(currentLine); + + // calculate how many rows the current wrapping line is using. + size_t old_numWrappingLines = calc_wrap(old_strlen); + size_t new_numWrappingLines = calc_wrap(new_strlen); + + auto numWrappingLines = (old_strlen > new_strlen ? old_numWrappingLines : new_numWrappingLines); + + // move up some number of lines, while clearing the line (note: we already cleared the first one, so skip that) + // TODO: if the entire context spans more than the terminal height, we need to stop, and only print out the bottom half! + + // what we want to do here is go all the way to the bottom of the string (regardless of cursor position), and clear it. + // then go up one line and repeat, basically clearing the whole thing. + qcmd(moveCursorDown(numWrappingLines - st->wrappedLineIdx - 1)); + // fprintf(stderr, "new wl: %zu, old wl: %zu, WLI: %zu, NWL: %zu\n", new_numWrappingLines, old_numWrappingLines, + // st->wrappedLineIdx, numWrappingLines); + + for(size_t i = 1; i < numWrappingLines; i++) + { + qcmd("\x1b[2K"); + qcmd(moveCursorUp(1)); + } + + + // move to the first position. + qcmd(moveCursorLeft(9999)); + flushcmd(); + + auto old_pos = getCursorPosition(); + + + // start dumping: + qcmd(pr); + + + size_t remaining = new_strlen; + size_t curlinecount = 1; + + calc_wli(st->cursor); + + while(true) + { + auto adv_and_cons = [¤tLine](size_t cons) -> std::string_view { + // return the view from [here, cons) -- in terms of codepoints + // and advance the line by cons codepoints. + + size_t bytes_to_adv = 0; + for(size_t i = 0; i < cons; i++) + { + bytes_to_adv = 1 + findEndOfUTF8CP(reinterpret_cast(currentLine.data()), + bytes_to_adv); + } + + auto ret = currentLine.substr(0, bytes_to_adv); + currentLine.remove_prefix(bytes_to_adv); + + return ret; + }; + + auto left = st->termWidth - (curlinecount == 1 ? promptLen : wrapPL); + auto todo = std::min(left, remaining); + + qcmd(adv_and_cons(todo)); + + remaining -= todo; + + if(remaining == 0) + { + // pad out the rest of the line with spaces. + for(size_t i = todo; i < left; i++) + qcmd(" "); + + break; + } + + // if there's more: + // move the cursor down and left. + qcmd(moveCursorDown(1)); + qcmd(moveCursorLeft(9999)); + + // print the wrap prompt. + qcmd(st->wrappedPromptString); + curlinecount += 1; + } + + // ok, now it's time to fix up the cursor... + { + size_t hcursor = promptLen; + size_t vcursor = 0; + size_t x = st->cursor; + + while(true) + { + auto left = st->termWidth - hcursor; + auto todo = std::min(left, x); + x -= todo; + + if(x == 0) + { + hcursor += todo; + if(hcursor == st->termWidth) + vcursor++, hcursor = wrapPL; + + break; + } + + // move to the next line. + vcursor += 1; + hcursor = wrapPL; + } + + // we know what to do now. first go back to the start: + qcmd(setCursorPosition(old_pos)); + + qcmd(moveCursorDown(st->wrappedLineIdx)); + qcmd(moveCursorRight(hcursor)); + } + + flushcmd(); + } + + + + + + // this is ugly!!! + static State* currentStateForSignal = 0; static bool read_line(State* st) { constexpr char CTRL_C = '\x03'; @@ -623,7 +882,36 @@ namespace detail constexpr char ENTER = '\x0D'; constexpr char BACKSPACE = '\x7F'; + // NOT THREAD SAFE!! enterRawMode(); + currentStateForSignal = st; + st->termWidth = getTerminalWidth(); + st->termHeight = getTerminalHeight(); + + platform_write(st->promptMode == 0 ? st->promptString : st->contPromptString); + st->lines.push_back(""); + + bool didSetSignalHandler = false; + + // time for some signalling! + struct sigaction new_sa { + .sa_flags = SA_RESTART, // this is important, if not read() will return EINTR (interrupted by signal) + .sa_handler = [](int sig) { + if(currentStateForSignal) + { + currentStateForSignal->termWidth = getTerminalWidth(); + currentStateForSignal->termHeight = getTerminalHeight(); + } + } + }; + + struct sigaction old_sa; + if(sigaction(SIGWINCH, &new_sa, &old_sa) == 0) + didSetSignalHandler = true; + + + + auto cursor_home = [&st]() { st->cursor = 0; @@ -633,8 +921,8 @@ namespace detail }; auto cursor_end = [&st]() { - st->cursor = displayedTextLength(st->currLine); - st->byteCursor = st->currLine.size(); + st->cursor = displayedTextLength(getCurLine(st)); + st->byteCursor = getCurLine(st).size(); refresh_line(st); }; @@ -643,7 +931,7 @@ namespace detail if(st->cursor > 0) { auto x = st->byteCursor - 1; - auto l = findBeginningOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); st->byteCursor -= (x - l + 1); st->cursor -= 1; @@ -652,10 +940,10 @@ namespace detail }; auto cursor_right = [&st]() { - if(st->byteCursor < st->currLine.size()) + if(st->byteCursor < getCurLine(st).size()) { auto x = st->byteCursor; - auto l = findEndOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); st->byteCursor += (l - x + 1); st->cursor += 1; @@ -664,32 +952,34 @@ namespace detail } }; - - auto delete_left = [&st]() { - if(st->cursor > 0 && st->currLine.size() > 0) + if(st->cursor > 0 && getCurLine(st).size() > 0) { auto x = st->byteCursor - 1; - auto l = findBeginningOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - st->currLine.erase(l, x - l + 1); + auto old = getCurLine(st); + + getCurLine(st).erase(l, x - l + 1); st->byteCursor -= (x - l + 1); st->cursor -= 1; - refresh_line(st); + refresh_line(st, &old); } }; auto delete_right = [&st]() { - if(st->byteCursor < st->currLine.size()) + if(st->byteCursor < getCurLine(st).size()) { auto x = st->byteCursor; - auto l = findEndOfUTF8CP(reinterpret_cast(st->currLine.c_str()), x); + auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - st->currLine.erase(x, l - x + 1); + auto old = getCurLine(st); + + getCurLine(st).erase(x, l - x + 1); // both cursors remain unchanged. - refresh_line(st); + refresh_line(st, &old); } }; @@ -714,29 +1004,35 @@ namespace detail goto finish; } - // this is a little complex; if there's stuff in the buffer, then don't do anything - // possibly do a bell -- swift and python both do this. + // this is a little complex; control-D is apparently both EOF: when the buffer is empty, + // and delete-left: when it is not... case CTRL_D: { - // if the buffer is empty, then quit. - if(st->currLine.empty()) + if(getCurLine(st).empty()) { eof = true; goto finish; } + else + { + delete_left(); + } } break; case CTRL_C: { - if(st->currLine.empty()) + if(getCurLine(st).empty()) { eof = true; goto finish; } else { - st->currLine.clear(); + auto old = getCurLine(st); + getCurLine(st).clear(); + st->cursor = 0; - refresh_line(st); + st->byteCursor = 0; + refresh_line(st, &old); } } @@ -797,10 +1093,12 @@ namespace detail // default: just append -- if it's not a control char. default: { + auto old = getCurLine(st); + uint8_t uc = static_cast(c); if(uc >= 0x20 && uc <= 0x7F) { - st->currLine.insert(st->currLine.begin() + st->byteCursor, c); + getCurLine(st).insert(getCurLine(st).begin() + st->byteCursor, c); st->cursor += 1; st->byteCursor += 1; @@ -831,18 +1129,26 @@ namespace detail cp += buf; } - st->currLine.insert(st->byteCursor, cp); + getCurLine(st).insert(st->byteCursor, cp); st->byteCursor += cp.size(); st->cursor += displayedTextLength(cp); } - refresh_line(st); + refresh_line(st, &old); } break; } } + + + finish: + // restore the signal state, and reset the terminal to normal mode. + currentStateForSignal = 0; + if(didSetSignalHandler) + sigaction(SIGWINCH, &old_sa, nullptr); + leaveRawMode(); return eof; } @@ -869,6 +1175,11 @@ namespace ztmu this->promptString = prompt; } + void State::setWrappedPrompt(const std::string& prompt) + { + this->wrappedPromptString = prompt; + } + void State::setContinuationPrompt(const std::string& prompt) { this->contPromptString = prompt; @@ -878,39 +1189,34 @@ namespace ztmu { // clear. this->clear(); + this->promptMode = 0; - detail::platform_write(this->promptString); bool eof = detail::read_line(this); if(!eof) { - this->lines.push_back(this->currLine); - this->currLine.clear(); - return this->lines.back(); } else { - return "\x4"; + return "\x04"; } } std::string State::readContinuation() { // don't clear. - detail::platform_write(this->contPromptString); + this->promptMode = 1; bool eof = detail::read_line(this); if(!eof) { - this->lines.push_back(this->currLine); - this->currLine.clear(); - + // DOES NOT WORK!!! return this->lines.back(); } else { - return "\x4"; + return "\x04"; } } } diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index e8b594d6..d9a0bf72 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -10,6 +10,8 @@ #define ZTMU_CREATE_IMPL 1 #include "ztmu.h" +#include "linenoise/linenoise.h" + namespace repl { static void runCommand(const std::string& s) @@ -20,6 +22,7 @@ namespace repl } static constexpr const char* PROMPT_STRING = COLOUR_BLUE " * " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* WRAP_PROMPT_STRING = COLOUR_GREY_BOLD " |" COLOUR_RESET " "; static constexpr const char* CONTINUATION_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; void start() @@ -33,14 +36,13 @@ namespace repl auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); + st.setWrappedPrompt(WRAP_PROMPT_STRING); st.setContinuationPrompt(CONTINUATION_PROMPT_STRING); printf("\nread: %s\n", st.read().c_str()); - - // printf("len = %zu\n", ztmu::detail::displayedTextLength(PROMPT_STRING)); From 0d8b0669bbfbec373df7370d0df24ee22c10ecb5 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 7 Nov 2019 13:37:32 +0800 Subject: [PATCH 058/129] up/down cursor --- source/include/ztmu.h | 383 +++++++++++++++++++++++++++++------------- 1 file changed, 270 insertions(+), 113 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index db9cfae2..b3e2b241 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -29,6 +29,18 @@ namespace ztmu void setWrappedPrompt(const std::string& prompt); void setContinuationPrompt(const std::string& prompt); + void move_cursor_left(int n); + void move_cursor_right(int n); + + void move_cursor_up(int n); + void move_cursor_down(int n); + + void move_cursor_home(); + void move_cursor_end(); + + void delete_left(int n); + void delete_right(int n); + // 0 = normal, 1 = continuation @@ -662,6 +674,33 @@ namespace detail } + static size_t get_cursor_line_offset(State* st, size_t num, size_t firstPromptLen, size_t subsequentPromptLen) + { + size_t hcursor = firstPromptLen; + size_t x = st->cursor; + + while(true) + { + auto left = st->termWidth - hcursor; + auto todo = std::min(left, x); + x -= todo; + + if(x == 0) + { + hcursor += todo; + if(hcursor == st->termWidth) + hcursor = subsequentPromptLen; + + break; + } + + hcursor = subsequentPromptLen; + } + + return hcursor; + } + + static void refresh_line(State* st, std::string* oldLine = 0) { @@ -834,38 +873,171 @@ namespace detail // ok, now it's time to fix up the cursor... { - size_t hcursor = promptLen; - size_t vcursor = 0; - size_t x = st->cursor; + auto hcursor = get_cursor_line_offset(st, st->cursor, promptLen, wrapPL); - while(true) + // we know what to do now. first go back to the start: + qcmd(setCursorPosition(old_pos)); + + qcmd(moveCursorDown(st->wrappedLineIdx)); + qcmd(moveCursorRight(hcursor)); + } + + flushcmd(); + } + + static void cursor_home(State* st) + { + st->cursor = 0; + st->byteCursor = 0; + + refresh_line(st); + } + + static void cursor_end(State* st) + { + st->cursor = displayedTextLength(getCurLine(st)); + st->byteCursor = getCurLine(st).size(); + + refresh_line(st); + } + + static void cursor_left(State* st, int n, bool refresh = true) + { + for(int i = 0; i < n; i++) + { + if(st->cursor > 0) { - auto left = st->termWidth - hcursor; - auto todo = std::min(left, x); - x -= todo; + auto x = st->byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); + st->byteCursor -= (x - l + 1); + st->cursor -= 1; + } + } - if(x == 0) - { - hcursor += todo; - if(hcursor == st->termWidth) - vcursor++, hcursor = wrapPL; + if(refresh) + refresh_line(st); + } - break; - } + static void cursor_right(State* st, int n, bool refresh = true) + { + for(int i = 0; i < n; i++) + { + if(st->byteCursor < getCurLine(st).size()) + { + auto x = st->byteCursor; + auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - // move to the next line. - vcursor += 1; - hcursor = wrapPL; + st->byteCursor += (l - x + 1); + st->cursor += 1; } + } - // we know what to do now. first go back to the start: - qcmd(setCursorPosition(old_pos)); + if(refresh) + refresh_line(st); + } - qcmd(moveCursorDown(st->wrappedLineIdx)); - qcmd(moveCursorRight(hcursor)); + static void delete_left(State* st) + { + if(st->cursor > 0 && getCurLine(st).size() > 0) + { + auto x = st->byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); + + auto old = getCurLine(st); + + getCurLine(st).erase(l, x - l + 1); + st->byteCursor -= (x - l + 1); + st->cursor -= 1; + + refresh_line(st, &old); + } + } + + static void delete_right(State* st) + { + if(st->byteCursor < getCurLine(st).size()) + { + auto x = st->byteCursor; + auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); + + auto old = getCurLine(st); + + getCurLine(st).erase(x, l - x + 1); + + // both cursors remain unchanged. + refresh_line(st, &old); } + } - flushcmd(); + static void cursor_up(State* st) + { + if(st->cursor > 0 && st->wrappedLineIdx > 0) + { + size_t promptL = displayedTextLength(st->promptMode == 0 ? st->promptString : st->contPromptString); + size_t wrapL = displayedTextLength(st->wrappedPromptString); + + // first, get the current horz cursor position: + auto hcursor = get_cursor_line_offset(st, st->cursor, promptL, wrapL); + + // because wrappedLineIdx > 0, we know we're wrapping. this is the number of chars on the current + // line (of text!), to the left of the cursor. + auto h_curlength = hcursor - wrapL; + + // this is how many extra chars to go left. + auto rightmargin = st->termWidth - hcursor; + + // this will stop when we can't go further, so there's no need to limit. + // note: this doesn't manipulate the console cursor at all -- we only do that in + // refresh_line, once! + cursor_left(st, h_curlength + rightmargin); + + /* + so what we want to do, ideally, is move "left" termWidth number of codepoints. HOWEVER, + if we have such a situation: + * > some long str + | ing + ^ + + if the cursor is there and we want to go "up", what ought to happen is that the cursor gets + moved to the left extreme -- ie. before the "s" in "some". we will make and document an + assumption here: all the wrapped prompts are the same length, and the only situation where + you get this problem is when you move from the second line up to the first line. thus, we + can use the length of the string, without having to do stupid weird math to figure out + how many chars are on the previous line. + */ + } + else + { + // TODO: handle moving up into the previous "line" list -- during continuations. + // TODO: possibly handle history? + } + } + + static void cursor_down(State* st) + { + if(st->byteCursor < getCurLine(st).size()) + { + // works on a similar principle as cursor_up. + size_t promptL = displayedTextLength(st->promptMode == 0 ? st->promptString : st->contPromptString); + size_t wrapL = displayedTextLength(st->wrappedPromptString); + + auto hcursor = get_cursor_line_offset(st, st->cursor, promptL, wrapL); + + // again, we make a similar assumption -- that only the first and second prompts can differ; the second and + // subsequent wrapping prompts must have the same length. + + auto rightmargin = st->termWidth - hcursor; + + // in the next line, the prompt will always be a wrapping prompt. + auto leftmargin = hcursor - wrapL; + + cursor_right(st, rightmargin + leftmargin); + } + else + { + // TODO: handle moving down into the next "line" list -- during continuations. + // TODO: possibly handle history? + } } @@ -913,82 +1085,6 @@ namespace detail - auto cursor_home = [&st]() { - st->cursor = 0; - st->byteCursor = 0; - - refresh_line(st); - }; - - auto cursor_end = [&st]() { - st->cursor = displayedTextLength(getCurLine(st)); - st->byteCursor = getCurLine(st).size(); - - refresh_line(st); - }; - - auto cursor_left = [&st]() { - if(st->cursor > 0) - { - auto x = st->byteCursor - 1; - auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - st->byteCursor -= (x - l + 1); - st->cursor -= 1; - - refresh_line(st); - } - }; - - auto cursor_right = [&st]() { - if(st->byteCursor < getCurLine(st).size()) - { - auto x = st->byteCursor; - auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - - st->byteCursor += (l - x + 1); - st->cursor += 1; - - refresh_line(st); - } - }; - - auto delete_left = [&st]() { - if(st->cursor > 0 && getCurLine(st).size() > 0) - { - auto x = st->byteCursor - 1; - auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - - auto old = getCurLine(st); - - getCurLine(st).erase(l, x - l + 1); - st->byteCursor -= (x - l + 1); - st->cursor -= 1; - - refresh_line(st, &old); - } - }; - - auto delete_right = [&st]() { - if(st->byteCursor < getCurLine(st).size()) - { - auto x = st->byteCursor; - auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - - auto old = getCurLine(st); - - getCurLine(st).erase(x, l - x + 1); - - // both cursors remain unchanged. - refresh_line(st, &old); - } - }; - - - - - - - bool eof = false; while(true) { @@ -1015,7 +1111,7 @@ namespace detail } else { - delete_left(); + delete_left(st); } } break; @@ -1039,7 +1135,7 @@ namespace detail // backspace case BACKSPACE: [[fallthrough]]; case BACKSPACE_: { - delete_left(); + delete_left(st); } break; // time for some fun. @@ -1063,30 +1159,32 @@ namespace detail switch(n) { - case 3: delete_right(); break; + case 3: delete_right(st); break; - case 1: cursor_home(); break; - case 7: cursor_home(); break; + case 1: cursor_home(st); break; + case 7: cursor_home(st); break; - case 4: cursor_end(); break; - case 8: cursor_end(); break; + case 4: cursor_end(st); break; + case 8: cursor_end(st); break; } } else { switch(seq[1]) { - case 'C': cursor_right(); break; - case 'D': cursor_left(); break; - case 'H': cursor_home(); break; - case 'F': cursor_end(); break; + case 'A': cursor_up(st); break; + case 'B': cursor_down(st); break; + case 'C': cursor_right(st, 1); break; + case 'D': cursor_left(st, 1); break; + case 'H': cursor_home(st); break; + case 'F': cursor_end(st); break; } } } else if(seq[0] == 'O') { - if(seq[1] == 'H') cursor_home(); - else if(seq[1] == 'F') cursor_end(); + if(seq[1] == 'H') cursor_home(st); + else if(seq[1] == 'F') cursor_end(st); } } break; @@ -1154,8 +1252,6 @@ namespace detail } - - #endif } @@ -1185,6 +1281,67 @@ namespace ztmu this->contPromptString = prompt; } + void State::move_cursor_left(int n) + { + if(n < 0) move_cursor_right(-n); + + detail::cursor_left(this, n); + } + + void State::move_cursor_right(int n) + { + if(n < 0) move_cursor_left(-n); + + detail::cursor_right(this, n); + } + + void State::move_cursor_up(int n) + { + if(n < 0) move_cursor_down(-n); + + for(int i = 0; i < n; i++) + detail::cursor_up(this); + } + + void State::move_cursor_down(int n) + { + if(n < 0) move_cursor_up(-n); + + for(int i = 0; i < n; i++) + detail::cursor_down(this); + } + + + void State::move_cursor_home() + { + detail::cursor_home(this); + } + + void State::move_cursor_end() + { + detail::cursor_end(this); + } + + void State::delete_left(int n) + { + if(n <= 0) return; + + for(int i = 0; i < n; i++) + detail::delete_left(this); + } + + void State::delete_right(int n) + { + if(n <= 0) return; + + for(int i = 0; i < n; i++) + detail::delete_right(this); + } + + + + + std::string State::read() { // clear. From 967a6677df61b14cd2427723c27f4b9fe9f9e67a Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 7 Nov 2019 14:07:55 +0800 Subject: [PATCH 059/129] continuations appear to work, basically --- source/frontend/parser/variable.cpp | 2 +- source/include/ztmu.h | 54 +++++++++++++---------- source/repl/driver.cpp | 67 +++++++++++++---------------- 3 files changed, 62 insertions(+), 61 deletions(-) diff --git a/source/frontend/parser/variable.cpp b/source/frontend/parser/variable.cpp index cdafc35e..75c1b060 100644 --- a/source/frontend/parser/variable.cpp +++ b/source/frontend/parser/variable.cpp @@ -168,7 +168,7 @@ namespace parser } else if(st.front() != TT::Identifier) { - expectedAfter(st, "identifier", "'" + std::string(isImmut ? "val" : "var") + "'", st.front().str()); + expectedAfter(st, "identifier", "'" + std::string(isImmut ? "let" : "var") + "'", st.front().str()); } auto loc = st.front().loc; diff --git a/source/include/ztmu.h b/source/include/ztmu.h index b3e2b241..9531ea51 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -13,6 +13,7 @@ #include #include +#include #include @@ -22,8 +23,8 @@ namespace ztmu { void clear(); - std::string read(); - std::string readContinuation(); + std::optional read(); + std::optional readContinuation(); void setPrompt(const std::string& prompt); void setWrappedPrompt(const std::string& prompt); @@ -1048,8 +1049,10 @@ namespace detail static State* currentStateForSignal = 0; static bool read_line(State* st) { + constexpr char CTRL_A = '\x01'; constexpr char CTRL_C = '\x03'; constexpr char CTRL_D = '\x04'; + constexpr char CTRL_E = '\x05'; constexpr char BACKSPACE_ = '\x08'; constexpr char ENTER = '\x0D'; constexpr char BACKSPACE = '\x7F'; @@ -1060,8 +1063,12 @@ namespace detail st->termWidth = getTerminalWidth(); st->termHeight = getTerminalHeight(); + st->cursor = 0; + st->byteCursor = 0; + st->wrappedLineIdx = 0; + st->lines.emplace_back(""); + platform_write(st->promptMode == 0 ? st->promptString : st->contPromptString); - st->lines.push_back(""); bool didSetSignalHandler = false; @@ -1115,6 +1122,14 @@ namespace detail } } break; + case CTRL_A: { + cursor_home(st); + } break; + + case CTRL_E: { + cursor_end(st); + } break; + case CTRL_C: { if(getCurLine(st).empty()) { @@ -1247,7 +1262,12 @@ namespace detail if(didSetSignalHandler) sigaction(SIGWINCH, &old_sa, nullptr); + // move the cursor to the end, refresh the line, then leave raw mode -- this makes sure + // that we leave the cursor in a nice place after the call to this function returns. + cursor_end(st); leaveRawMode(); + + platform_write("\n"); return eof; } @@ -1264,6 +1284,7 @@ namespace ztmu { // reset the thing. this->lines.clear(); + this->lineIdx = 0; } void State::setPrompt(const std::string& prompt) @@ -1342,7 +1363,7 @@ namespace ztmu - std::string State::read() + std::optional State::read() { // clear. this->clear(); @@ -1350,31 +1371,20 @@ namespace ztmu bool eof = detail::read_line(this); - if(!eof) - { - return this->lines.back(); - } - else - { - return "\x04"; - } + if(eof) return std::nullopt; + else return this->lines.back(); } - std::string State::readContinuation() + std::optional State::readContinuation() { // don't clear. this->promptMode = 1; + this->lineIdx++; + bool eof = detail::read_line(this); - if(!eof) - { - // DOES NOT WORK!!! - return this->lines.back(); - } - else - { - return "\x04"; - } + if(eof) return std::nullopt; + else return this->lines.back(); } } diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index d9a0bf72..3a4976a3 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -39,44 +39,35 @@ namespace repl st.setWrappedPrompt(WRAP_PROMPT_STRING); st.setContinuationPrompt(CONTINUATION_PROMPT_STRING); - printf("\nread: %s\n", st.read().c_str()); - - - - // printf("len = %zu\n", ztmu::detail::displayedTextLength(PROMPT_STRING)); - - - - - // while(auto line = (PROMPT_STRING)) - // { - // input += std::string(line); - - // if(input.empty()) - // continue; - - // if(input[0] == ':') - // { - // runCommand(input.substr(1)); - // printf("\n"); - // continue; - // } - - // if(bool needmore = processLine(input + "\n"); needmore) - // { - // // read more. - // while(needmore) - // { - // auto line = (CONTINUATION_PROMPT_STRING); - // input += std::string(line) + "\n"; - - // needmore = processLine(input); - // } - // } - - // // ok, we're done -- clear. - // input.clear(); - // } + while(auto line = st.read()) + { + input += std::string(*line); + + if(input.empty()) + continue; + + if(input[0] == ':') + { + runCommand(input.substr(1)); + printf("\n"); + } + else if(bool needmore = processLine(input += "\n"); needmore) + { + // read more. + while(auto line = st.readContinuation()) + { + input += std::string(*line) + "\n"; + + needmore = processLine(input); + + if(!needmore) + break; + } + } + + // ok, we're done -- clear. + input.clear(); + } } } From 6290569533850910018d9c14b4cceb066eccdb90 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 9 Nov 2019 12:08:58 +0800 Subject: [PATCH 060/129] okkkkkkk rewrite-ish --- source/include/ztmu.h | 342 ++++++++++++++++++++++++++++-------------- 1 file changed, 232 insertions(+), 110 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 9531ea51..d6fdabce 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -43,10 +43,6 @@ namespace ztmu void delete_right(int n); - - // 0 = normal, 1 = continuation - int promptMode = 0; - std::string promptString; std::string contPromptString; std::string wrappedPromptString; @@ -675,10 +671,45 @@ namespace detail } - static size_t get_cursor_line_offset(State* st, size_t num, size_t firstPromptLen, size_t subsequentPromptLen) + static std::pair calculate_left_codepoints(int n, const std::string_view& sv, size_t cursor, size_t byteCursor) + { + for(int i = 0; i < n; i++) + { + if(cursor > 0) + { + auto x = byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(sv.data()), x); + byteCursor -= (x - l + 1); + cursor -= 1; + } + } + + return { cursor, byteCursor }; + } + + static std::pair calculate_right_codepoints(int n, const std::string_view& sv, size_t cursor, size_t byteCursor) + { + for(int i = 0; i < n; i++) + { + if(byteCursor < sv.size()) + { + auto x = byteCursor; + auto l = findEndOfUTF8CP(reinterpret_cast(sv.data()), x); + + byteCursor += (l - x + 1); + cursor += 1; + } + } + + return { cursor, byteCursor }; + } + + + + static size_t get_cursor_line_offset(State* st, size_t num, size_t firstPromptLen, size_t subsequentPromptLen, bool* didWrap = 0) { size_t hcursor = firstPromptLen; - size_t x = st->cursor; + size_t x = num; while(true) { @@ -690,7 +721,10 @@ namespace detail { hcursor += todo; if(hcursor == st->termWidth) + { hcursor = subsequentPromptLen; + if(didWrap) *didWrap = true; + } break; } @@ -705,12 +739,8 @@ namespace detail static void refresh_line(State* st, std::string* oldLine = 0) { - // what we want to do is write the entire command + text string at once to reduce flickering. - // for now, meh. - - auto pr = st->promptMode == 0 ? st->promptString : st->contPromptString; - auto promptLen = displayedTextLength(pr); - + auto normPL = displayedTextLength(st->promptString); + auto contPL = displayedTextLength(st->contPromptString); auto wrapPL = displayedTextLength(st->wrappedPromptString); if(!oldLine) @@ -733,13 +763,11 @@ namespace detail auto calc_wrap = [&](size_t len) -> size_t { auto width = st->termWidth; - auto norm_pl = promptLen; - auto wrap_pl = displayedTextLength(st->wrappedPromptString); size_t lines = 1; while(len > 0) { - auto left = width - (lines == 1 ? norm_pl : wrap_pl); + auto left = width - (lines == 1 ? normPL : wrapPL); if(left == len) lines += 1; @@ -753,20 +781,20 @@ namespace detail return lines; }; - auto calc_wli = [&](size_t len) { + auto calc_wli = [&](size_t len) -> size_t { size_t done = 0; size_t remaining = len; size_t lc = 1; - st->wrappedLineIdx = 0; + size_t wli = 0; while(true) { - auto left = st->termWidth - (lc == 1 ? promptLen : wrapPL); + auto left = st->termWidth - (lc == 1 ? normPL : wrapPL); auto todo = std::min(left, remaining); if(remaining == left) - st->wrappedLineIdx++; + wli++; remaining -= todo; done += todo; @@ -777,112 +805,178 @@ namespace detail // if your cursor is beyond this point, then you are on the next line. if(st->cursor >= done + 1) { - st->wrappedLineIdx++; + wli++; lc++; } } + + return wli; + }; + + auto getPromptForLine = [&](size_t wli) -> std::pair { + if(st->lineIdx == 0) + { + if(wli == 0) return { st->promptString, normPL }; + else return { st->wrappedPromptString, wrapPL }; + } + else + { + if(wli == 0) return { st->contPromptString, contPL }; + else return { st->wrappedPromptString, wrapPL }; + } }; std::string_view currentLine = getCurLine(st); - auto old_strlen = displayedTextLength(*oldLine); - auto new_strlen = displayedTextLength(currentLine); - - // calculate how many rows the current wrapping line is using. - size_t old_numWrappingLines = calc_wrap(old_strlen); - size_t new_numWrappingLines = calc_wrap(new_strlen); + size_t numWrappingLines = calc_wrap(displayedTextLength(currentLine)); + size_t old_NWL = calc_wrap(displayedTextLength(*oldLine)); - auto numWrappingLines = (old_strlen > new_strlen ? old_numWrappingLines : new_numWrappingLines); + auto new_wli = calc_wli(st->cursor); - // move up some number of lines, while clearing the line (note: we already cleared the first one, so skip that) - // TODO: if the entire context spans more than the terminal height, we need to stop, and only print out the bottom half! + fprintf(stderr, "nwl: %zu, old_nwl: %zu, new_wli: %zu, old_wli: %zu\n", numWrappingLines, old_NWL, new_wli, st->wrappedLineIdx); // what we want to do here is go all the way to the bottom of the string (regardless of cursor position), and clear it. - // then go up one line and repeat, basically clearing the whole thing. - qcmd(moveCursorDown(numWrappingLines - st->wrappedLineIdx - 1)); - // fprintf(stderr, "new wl: %zu, old wl: %zu, WLI: %zu, NWL: %zu\n", new_numWrappingLines, old_numWrappingLines, - // st->wrappedLineIdx, numWrappingLines); - - for(size_t i = 1; i < numWrappingLines; i++) + // then go up one line and repeat, basically clearing the whole thing. we don't clear lines above the cursor! { - qcmd("\x1b[2K"); - qcmd(moveCursorUp(1)); + auto nwl = std::max(numWrappingLines, old_NWL); + + if(numWrappingLines > st->wrappedLineIdx) + qcmd(moveCursorDown(nwl - st->wrappedLineIdx - 1)); + + for(size_t i = 1; i < nwl - new_wli; i++) + { + qcmd("\x1b[2K"); + qcmd(moveCursorUp(1)); + } } - // move to the first position. + // move to the leftmost position. qcmd(moveCursorLeft(9999)); - flushcmd(); - auto old_pos = getCursorPosition(); + { + // there's stuff on the left of the cursor that we need to print too, since we cleared the entire line. + bool didWrap = false; + size_t alrPrinted = 0; - // start dumping: - qcmd(pr); + // TODO: we always use normPL here, but we should choose between normPL and contPL! + auto hcursor = get_cursor_line_offset(st, st->cursor, normPL, wrapPL, &didWrap); + fprintf(stderr, "didWrap: %d\n", didWrap); + if(didWrap) + { + // we always use new_wli - 1, because we might be moving left-to-right or right-to-left! + auto [ pstr, plen ] = getPromptForLine(new_wli - 1); - size_t remaining = new_strlen; - size_t curlinecount = 1; + auto leftCPs = st->termWidth - plen; + auto leftByteCursor = calculate_left_codepoints(leftCPs, currentLine, st->cursor, st->byteCursor).second; + auto leftPortion = currentLine.substr(leftByteCursor, st->byteCursor - leftByteCursor); + currentLine.remove_prefix(st->byteCursor); - calc_wli(st->cursor); + // before proceeding, move the cursor up one, and to the left. + qcmd(moveCursorUp(1)); + qcmd(moveCursorLeft(9999)); - while(true) - { - auto adv_and_cons = [¤tLine](size_t cons) -> std::string_view { - // return the view from [here, cons) -- in terms of codepoints - // and advance the line by cons codepoints. + qcmd(pstr); + qcmd(leftPortion); - size_t bytes_to_adv = 0; - for(size_t i = 0; i < cons; i++) - { - bytes_to_adv = 1 + findEndOfUTF8CP(reinterpret_cast(currentLine.data()), - bytes_to_adv); - } + // ok, now move the line down, and print the continuation prompt. + qcmd(moveCursorDown(1)); + qcmd(moveCursorLeft(9999)); + qcmd(st->wrappedPromptString); - auto ret = currentLine.substr(0, bytes_to_adv); - currentLine.remove_prefix(bytes_to_adv); + alrPrinted = wrapPL; + } + else + { + auto [ pstr, plen ] = getPromptForLine(new_wli); - return ret; - }; + auto leftCPs = hcursor - plen; + auto leftByteCursor = calculate_left_codepoints(leftCPs, currentLine, st->cursor, st->byteCursor).second; - auto left = st->termWidth - (curlinecount == 1 ? promptLen : wrapPL); - auto todo = std::min(left, remaining); + // we need to print leftCPs of stuff *before* the current cursor. + auto leftPortion = currentLine.substr(leftByteCursor, st->byteCursor - leftByteCursor); + currentLine.remove_prefix(st->byteCursor); - qcmd(adv_and_cons(todo)); + auto rem = displayedTextLength(currentLine); + fprintf(stderr, "hcursor = %zu, remaining = %zu, bc = %zu, lbc = %zu, len = %zu, line = '%.*s', pr = '%s', plen = %zu\n", + hcursor, rem, st->byteCursor, leftByteCursor, currentLine.length(), static_cast(currentLine.length()), + currentLine.data(), pstr.c_str(), plen); - remaining -= todo; + fprintf(stderr, "leftportion = '%s'\n", std::string(leftPortion).c_str()); - if(remaining == 0) - { - // pad out the rest of the line with spaces. - for(size_t i = todo; i < left; i++) - qcmd(" "); + qcmd(pstr); + qcmd(leftPortion); - break; + alrPrinted = plen + displayedTextLength(leftPortion); } - // if there's more: - // move the cursor down and left. - qcmd(moveCursorDown(1)); - qcmd(moveCursorLeft(9999)); + size_t printedLines = 0; + size_t remaining = displayedTextLength(currentLine); - // print the wrap prompt. - qcmd(st->wrappedPromptString); - curlinecount += 1; - } + // clear to the right, just in case + qcmd(zpr::sprint("%s0K", CSI)); - // ok, now it's time to fix up the cursor... - { - auto hcursor = get_cursor_line_offset(st, st->cursor, promptLen, wrapPL); + while(remaining > 0) + { + // ok, now we can print the rest of the string "normally". + auto adv_and_cons = [¤tLine](size_t cons) -> std::string_view { + // return the view from [here, cons) -- in terms of codepoints + // and advance the line by cons codepoints. + + size_t bytes_to_adv = 0; + for(size_t i = 0; i < cons; i++) + { + bytes_to_adv = 1 + findEndOfUTF8CP(reinterpret_cast(currentLine.data()), + bytes_to_adv); + } + + auto ret = currentLine.substr(0, bytes_to_adv); + currentLine.remove_prefix(bytes_to_adv); - // we know what to do now. first go back to the start: - qcmd(setCursorPosition(old_pos)); + return ret; + }; - qcmd(moveCursorDown(st->wrappedLineIdx)); + auto left = st->termWidth - (printedLines == 0 ? alrPrinted : wrapPL); + auto todo = std::min(left, remaining); + + qcmd(adv_and_cons(todo)); + remaining -= todo; + + if(remaining == 0) + { + // clear the rest of the line (cursor to end) + if(todo != left) + qcmd(zpr::sprint("%s0K", CSI)); + + fprintf(stderr, "broke\n"); + break; + } + + // if there's more: + // move the cursor down and left. + qcmd(moveCursorDown(1)); + qcmd(moveCursorLeft(9999)); + + // print the wrap prompt. + qcmd(st->wrappedPromptString); + printedLines += 1; + } + + qcmd(moveCursorLeft(9999)); + + // ok time to move up... we know that we padded with spaces all the way to the edge + // so we can move up by printedLines, and left by width - hcursor + qcmd(moveCursorUp(static_cast(printedLines))); + qcmd(moveCursorLeft(9999)); qcmd(moveCursorRight(hcursor)); + fprintf(stderr, "hcursor = %zu\n", hcursor); } + st->wrappedLineIdx = calc_wli(st->cursor); + fprintf(stderr, "updated wli: %zu\n\n", st->wrappedLineIdx); flushcmd(); } @@ -974,7 +1068,8 @@ namespace detail { if(st->cursor > 0 && st->wrappedLineIdx > 0) { - size_t promptL = displayedTextLength(st->promptMode == 0 ? st->promptString : st->contPromptString); + // TODO: + size_t promptL = displayedTextLength(st->promptString); size_t wrapL = displayedTextLength(st->wrappedPromptString); // first, get the current horz cursor position: @@ -1007,9 +1102,12 @@ namespace detail how many chars are on the previous line. */ } + else if(st->lineIdx > 0) + { + // ok -- we are now into weird strange territory. move up into the previous continuation line... + } else { - // TODO: handle moving up into the previous "line" list -- during continuations. // TODO: possibly handle history? } } @@ -1019,7 +1117,14 @@ namespace detail if(st->byteCursor < getCurLine(st).size()) { // works on a similar principle as cursor_up. - size_t promptL = displayedTextLength(st->promptMode == 0 ? st->promptString : st->contPromptString); + + // TODO: + size_t promptL = 0; + { + + } + + promptL = displayedTextLength(st->promptString); size_t wrapL = displayedTextLength(st->wrappedPromptString); auto hcursor = get_cursor_line_offset(st, st->cursor, promptL, wrapL); @@ -1047,7 +1152,7 @@ namespace detail // this is ugly!!! static State* currentStateForSignal = 0; - static bool read_line(State* st) + static bool read_line(State* st, int promptMode) { constexpr char CTRL_A = '\x01'; constexpr char CTRL_C = '\x03'; @@ -1068,7 +1173,7 @@ namespace detail st->wrappedLineIdx = 0; st->lines.emplace_back(""); - platform_write(st->promptMode == 0 ? st->promptString : st->contPromptString); + platform_write(promptMode == 0 ? st->promptString : st->contPromptString); bool didSetSignalHandler = false; @@ -1158,6 +1263,7 @@ namespace detail // there should be at least two more! char seq[2]; platform_read(seq, 2); + char thing = 0; if(seq[0] == '[') { if(seq[1] >= '0' && seq[1] <= '9') @@ -1166,34 +1272,53 @@ namespace detail // ok now time to read until a ~ char x = seq[1]; - while(x != '~') + while(x >= '0' && x <= '9') { n = (10 * n) + (x - '0'); platform_read_one(&x); } - switch(n) + // we have a modifier -- for now, ignore them. + if(x == ';') { - case 3: delete_right(st); break; + // advance it. + platform_read_one(&x); - case 1: cursor_home(st); break; - case 7: cursor_home(st); break; + int mods = 0; + while(x >= '0' && x <= '9') + { + mods = (10 * mods) + (x - '0'); + platform_read_one(&x); + } - case 4: cursor_end(st); break; - case 8: cursor_end(st); break; + thing = x; + } + else + { + thing = n; } } else { - switch(seq[1]) - { - case 'A': cursor_up(st); break; - case 'B': cursor_down(st); break; - case 'C': cursor_right(st, 1); break; - case 'D': cursor_left(st, 1); break; - case 'H': cursor_home(st); break; - case 'F': cursor_end(st); break; - } + thing = seq[1]; + } + + switch(thing) + { + case 3: delete_right(st); break; + + case 1: cursor_home(st); break; + case 7: cursor_home(st); break; + + case 4: cursor_end(st); break; + case 8: cursor_end(st); break; + + case 'A': cursor_up(st); break; + case 'B': cursor_down(st); break; + case 'C': cursor_right(st, 1); break; + case 'D': cursor_left(st, 1); break; + case 'H': cursor_home(st); break; + case 'F': cursor_end(st); break; } } else if(seq[0] == 'O') @@ -1367,9 +1492,7 @@ namespace ztmu { // clear. this->clear(); - this->promptMode = 0; - - bool eof = detail::read_line(this); + bool eof = detail::read_line(this, /* promptMode: */ 0); if(eof) return std::nullopt; else return this->lines.back(); @@ -1378,10 +1501,9 @@ namespace ztmu std::optional State::readContinuation() { // don't clear. - this->promptMode = 1; this->lineIdx++; - bool eof = detail::read_line(this); + bool eof = detail::read_line(this, /* promptMode: */ 1); if(eof) return std::nullopt; else return this->lines.back(); From f97b051ff2a0e456c6c50b66ed5e89ff95fd7d6c Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 11 Nov 2019 12:56:06 +0800 Subject: [PATCH 061/129] screen now scrolls on overflow --- source/include/ztmu.h | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index d6fdabce..9f021d41 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -757,10 +757,6 @@ namespace detail }; - // kill the entire line, and move the cursor to the beginning as well. - qcmd("\x1b[2K"); - qcmd(moveCursorLeft(9999)); - auto calc_wrap = [&](size_t len) -> size_t { auto width = st->termWidth; @@ -834,6 +830,11 @@ namespace detail auto new_wli = calc_wli(st->cursor); + // kill the entire line, and move the cursor to the beginning as well. + // qcmd("\x1b[2K"); + qcmd(moveCursorLeft(9999)); + + fprintf(stderr, "nwl: %zu, old_nwl: %zu, new_wli: %zu, old_wli: %zu\n", numWrappingLines, old_NWL, new_wli, st->wrappedLineIdx); // what we want to do here is go all the way to the bottom of the string (regardless of cursor position), and clear it. @@ -842,7 +843,21 @@ namespace detail auto nwl = std::max(numWrappingLines, old_NWL); if(numWrappingLines > st->wrappedLineIdx) - qcmd(moveCursorDown(nwl - st->wrappedLineIdx - 1)); + { + // the the current position (just the vcursor): + auto vcursor = getCursorPosition().y; + auto tomove = nwl - st->wrappedLineIdx - 1; + + fprintf(stderr, "height: %zu, vc: %zu, tm: %zu\n", st->termHeight, vcursor, tomove); + if(st->termHeight - vcursor < tomove) + { + fprintf(stderr, "need to scroll\n"); + // scroll the screen down -- but move the text *up* + qcmd(zpr::sprint("%s%dS", CSI, tomove - (st->termHeight - vcursor))); + } + + qcmd(moveCursorDown(tomove)); + } for(size_t i = 1; i < nwl - new_wli; i++) { From a77873bb2117e3d17a9443a0535c7ecda12022d5 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 11 Nov 2019 18:34:42 +0800 Subject: [PATCH 062/129] tmp work saving --- makefile | 10 ++- source/include/platform.h | 20 ++++-- source/include/ztmu.h | 141 +++++++++++++++++++++++++------------- source/repl/driver.cpp | 53 +++++++++++++- 4 files changed, 164 insertions(+), 60 deletions(-) diff --git a/makefile b/makefile index 060f6b7c..b793a66c 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ CXX ?= "clang++" LLVM_CONFIG ?= "llvm-config" -CXXSRC := $(shell find source external -iname "*.cpp") +CXXSRC := $(shell find source external/tinyprocesslib -iname "*.cpp") CXXOBJ := $(CXXSRC:.cpp=.cpp.o) CXXDEPS := $(CXXSRC:.cpp=.cpp.d) @@ -86,6 +86,7 @@ endif UTF8REWIND_AR := external/libutf8rewind.a LINENOISE_AR := external/liblinenoise.a +REPLXX_AR := external/libreplxx.a FLXFLAGS += -sysroot $(SYSROOT) --ffi-escape @@ -145,10 +146,10 @@ copylibs: $(FLXSRC) @mv $(FLXLIBLOCATION)/libs $(FLXLIBLOCATION)/flaxlibs -$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(LINENOISE_AR) +$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(LINENOISE_AR) $(REPLXX_AR) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind -llinenoise + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind -llinenoise -lreplxx %.cpp.o: %.cpp @@ -167,6 +168,9 @@ $(UTF8REWIND_AR): $(LINENOISE_AR): @make -C external/linenoise all +$(REPLXX_AR): $(shell find external/replxx -name "*.cpp" -or -name "*.hxx") + @make -C external/replxx all + # haha clena: clean clean: diff --git a/source/include/platform.h b/source/include/platform.h index 6a37d095..6ff8e832 100644 --- a/source/include/platform.h +++ b/source/include/platform.h @@ -8,8 +8,6 @@ #include "defs.h" -namespace platform -{ #define ALLOCATE_MEMORY_FUNC "malloc" #define REALLOCATE_MEMORY_FUNC "realloc" #define FREE_MEMORY_FUNC "free" @@ -49,23 +47,31 @@ namespace platform #define PLATFORM_EXPORT_FUNCTION extern "C" __declspec(dllexport) - std::wstring convertStringToWChar(const std::string& s); - std::string convertWCharToString(const std::wstring& s); + namespace platform + { + std::wstring convertStringToWChar(const std::string& s); + std::string convertWCharToString(const std::wstring& s); + } #else #include #include - using filehandle_t = int; + namespace platform + { + using filehandle_t = int; + } #define CRT_FDOPEN "fdopen" #define PLATFORM_NEWLINE "\n" #define PLATFORM_EXPORT_FUNCTION extern "C" __attribute__((visibility("default"))) #endif - extern filehandle_t InvalidFileHandle; - #define REFCOUNT_SIZE 8 +namespace platform +{ + extern filehandle_t InvalidFileHandle; + filehandle_t openFile(const char* name, int mode, int flags); void closeFile(filehandle_t fd); diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 9f021d41..f7a8d9e5 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -24,11 +24,9 @@ namespace ztmu void clear(); std::optional read(); - std::optional readContinuation(); void setPrompt(const std::string& prompt); void setWrappedPrompt(const std::string& prompt); - void setContinuationPrompt(const std::string& prompt); void move_cursor_left(int n); void move_cursor_right(int n); @@ -42,9 +40,9 @@ namespace ztmu void delete_left(int n); void delete_right(int n); + void setEnterHandler(std::function handler); std::string promptString; - std::string contPromptString; std::string wrappedPromptString; size_t cursor = 0; @@ -57,6 +55,7 @@ namespace ztmu size_t termHeight = 0; std::vector lines; + std::function enterHandler; }; } @@ -670,6 +669,15 @@ namespace detail return i; } + static size_t convertCursorToByteCursor(const uint8_t* bytes, size_t cursor) + { + size_t bc = 0; + for(size_t i = 0; i < cursor; i++) + bc = 1 + findEndOfUTF8CP(bytes, bc); + + return bc; + } + static std::pair calculate_left_codepoints(int n, const std::string_view& sv, size_t cursor, size_t byteCursor) { @@ -706,21 +714,21 @@ namespace detail - static size_t get_cursor_line_offset(State* st, size_t num, size_t firstPromptLen, size_t subsequentPromptLen, bool* didWrap = 0) + static size_t get_cursor_line_offset(size_t termWidth, size_t num, size_t firstPromptLen, size_t subsequentPromptLen, bool* didWrap = 0) { size_t hcursor = firstPromptLen; size_t x = num; while(true) { - auto left = st->termWidth - hcursor; + auto left = termWidth - hcursor; auto todo = std::min(left, x); x -= todo; if(x == 0) { hcursor += todo; - if(hcursor == st->termWidth) + if(hcursor == termWidth) { hcursor = subsequentPromptLen; if(didWrap) *didWrap = true; @@ -737,14 +745,12 @@ namespace detail - static void refresh_line(State* st, std::string* oldLine = 0) + static void _refresh_line(State* st, size_t lineIdx, std::string* oldLine = 0) { auto normPL = displayedTextLength(st->promptString); auto contPL = displayedTextLength(st->contPromptString); auto wrapPL = displayedTextLength(st->wrappedPromptString); - if(!oldLine) - oldLine = &getCurLine(st); std::string commands; auto qcmd = [&commands](const std::string_view& c) { @@ -810,7 +816,7 @@ namespace detail }; auto getPromptForLine = [&](size_t wli) -> std::pair { - if(st->lineIdx == 0) + if(lineIdx == 0) { if(wli == 0) return { st->promptString, normPL }; else return { st->wrappedPromptString, wrapPL }; @@ -822,19 +828,19 @@ namespace detail } }; + if(!oldLine) + oldLine = &st->lines[lineIdx]; - std::string_view currentLine = getCurLine(st); + + std::string_view currentLine = st->lines[lineIdx]; size_t numWrappingLines = calc_wrap(displayedTextLength(currentLine)); size_t old_NWL = calc_wrap(displayedTextLength(*oldLine)); auto new_wli = calc_wli(st->cursor); - // kill the entire line, and move the cursor to the beginning as well. - // qcmd("\x1b[2K"); qcmd(moveCursorLeft(9999)); - fprintf(stderr, "nwl: %zu, old_nwl: %zu, new_wli: %zu, old_wli: %zu\n", numWrappingLines, old_NWL, new_wli, st->wrappedLineIdx); // what we want to do here is go all the way to the bottom of the string (regardless of cursor position), and clear it. @@ -848,7 +854,7 @@ namespace detail auto vcursor = getCursorPosition().y; auto tomove = nwl - st->wrappedLineIdx - 1; - fprintf(stderr, "height: %zu, vc: %zu, tm: %zu\n", st->termHeight, vcursor, tomove); + // fprintf(stderr, "height: %zu, vc: %zu, tm: %zu\n", st->termHeight, vcursor, tomove); if(st->termHeight - vcursor < tomove) { fprintf(stderr, "need to scroll\n"); @@ -877,7 +883,7 @@ namespace detail size_t alrPrinted = 0; // TODO: we always use normPL here, but we should choose between normPL and contPL! - auto hcursor = get_cursor_line_offset(st, st->cursor, normPL, wrapPL, &didWrap); + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, normPL, wrapPL, &didWrap); fprintf(stderr, "didWrap: %d\n", didWrap); if(didWrap) @@ -907,14 +913,21 @@ namespace detail else { auto [ pstr, plen ] = getPromptForLine(new_wli); + fprintf(stderr, "aaa\n"); auto leftCPs = hcursor - plen; auto leftByteCursor = calculate_left_codepoints(leftCPs, currentLine, st->cursor, st->byteCursor).second; + fprintf(stderr, "bbb\n"); // we need to print leftCPs of stuff *before* the current cursor. auto leftPortion = currentLine.substr(leftByteCursor, st->byteCursor - leftByteCursor); + fprintf(stderr, "ccc\n"); + fprintf(stderr, "ddd li=%zu, (%zu, %zu) - '%s'\n", lineIdx, st->byteCursor, currentLine.size(), + std::string(currentLine).c_str()); + currentLine.remove_prefix(st->byteCursor); + auto rem = displayedTextLength(currentLine); fprintf(stderr, "hcursor = %zu, remaining = %zu, bc = %zu, lbc = %zu, len = %zu, line = '%.*s', pr = '%s', plen = %zu\n", hcursor, rem, st->byteCursor, leftByteCursor, currentLine.length(), static_cast(currentLine.length()), @@ -995,6 +1008,32 @@ namespace detail flushcmd(); } + static void refresh_line(State* st, std::string* oldLine = 0) + { + // refresh the top line manually: + _refresh_line(st, st->lineIdx, oldLine); + + // this is not re-entrant!!! + auto old_wli = st->wrappedLineIdx; + auto old_bcr = st->byteCursor; + auto old_cur = st->cursor; + + // refresh every line including and after the current line. + for(size_t i = st->lineIdx + 1; i < st->lines.size(); i++) + { + st->wrappedLineIdx = 0; + st->byteCursor = 0; + st->cursor = 0; + + fprintf(stderr, "** refreshing line %zu...\n", i); + _refresh_line(st, i, &st->lines[i]); + } + + st->wrappedLineIdx = old_wli; + st->byteCursor = old_bcr; + st->cursor = old_cur; + } + static void cursor_home(State* st) { st->cursor = 0; @@ -1083,12 +1122,11 @@ namespace detail { if(st->cursor > 0 && st->wrappedLineIdx > 0) { - // TODO: - size_t promptL = displayedTextLength(st->promptString); + size_t promptL = displayedTextLength(st->lineIdx == 0 ? st->promptString : st->contPromptString); size_t wrapL = displayedTextLength(st->wrappedPromptString); // first, get the current horz cursor position: - auto hcursor = get_cursor_line_offset(st, st->cursor, promptL, wrapL); + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, wrapL); // because wrappedLineIdx > 0, we know we're wrapping. this is the number of chars on the current // line (of text!), to the left of the cursor. @@ -1098,8 +1136,6 @@ namespace detail auto rightmargin = st->termWidth - hcursor; // this will stop when we can't go further, so there's no need to limit. - // note: this doesn't manipulate the console cursor at all -- we only do that in - // refresh_line, once! cursor_left(st, h_curlength + rightmargin); /* @@ -1120,6 +1156,37 @@ namespace detail else if(st->lineIdx > 0) { // ok -- we are now into weird strange territory. move up into the previous continuation line... + fprintf(stderr, "going up...\n"); + + std::string_view prevLine = st->lines[st->lineIdx - 1]; + size_t prevLineLen = displayedTextLength(prevLine); + + size_t promptL = displayedTextLength(st->contPromptString); + size_t wrapL = displayedTextLength(st->wrappedPromptString); + + // first, get the current horz cursor position: + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, wrapL); + + // now, we get the offset of the previous line: + auto prev_hcursor = get_cursor_line_offset(st->termWidth, prevLineLen, + st->lineIdx > 1 ? promptL : displayedTextLength(st->promptString), wrapL); + + auto prev_right_margin = st->termWidth - prev_hcursor; + + st->cursor = prevLineLen; + st->byteCursor = convertCursorToByteCursor(reinterpret_cast(prevLine.data()), st->cursor); + fprintf(stderr, "c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, + prev_right_margin, std::string(prevLine).c_str()); + + st->lineIdx -= 1; + + // and then we move the cursor. + platform_write(moveCursorUp(1)); + cursor_left(st, st->termWidth - hcursor - prev_right_margin, /* refresh: */ false); + _refresh_line(st, st->lineIdx); + + fprintf(stderr, "c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, + prev_right_margin, std::string(prevLine).c_str()); } else { @@ -1134,15 +1201,10 @@ namespace detail // works on a similar principle as cursor_up. // TODO: - size_t promptL = 0; - { - - } - - promptL = displayedTextLength(st->promptString); + size_t promptL = displayedTextLength(st->lineIdx == 0 ? st->promptString : st->contPromptString); size_t wrapL = displayedTextLength(st->wrappedPromptString); - auto hcursor = get_cursor_line_offset(st, st->cursor, promptL, wrapL); + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, wrapL); // again, we make a similar assumption -- that only the first and second prompts can differ; the second and // subsequent wrapping prompts must have the same length. @@ -1437,11 +1499,6 @@ namespace ztmu this->wrappedPromptString = prompt; } - void State::setContinuationPrompt(const std::string& prompt) - { - this->contPromptString = prompt; - } - void State::move_cursor_left(int n) { if(n < 0) move_cursor_right(-n); @@ -1499,9 +1556,10 @@ namespace ztmu detail::delete_right(this); } - - - + void State::setEnterHandler(std::function handler) + { + this->enterHandler = handler; + } std::optional State::read() { @@ -1512,17 +1570,6 @@ namespace ztmu if(eof) return std::nullopt; else return this->lines.back(); } - - std::optional State::readContinuation() - { - // don't clear. - this->lineIdx++; - - bool eof = detail::read_line(this, /* promptMode: */ 1); - - if(eof) return std::nullopt; - else return this->lines.back(); - } } diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 3a4976a3..c8324261 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. -#include +#include #include "repl.h" #include "frontend.h" @@ -10,7 +10,7 @@ #define ZTMU_CREATE_IMPL 1 #include "ztmu.h" -#include "linenoise/linenoise.h" +#include "replxx/include/replxx.hxx" namespace repl { @@ -25,6 +25,7 @@ namespace repl static constexpr const char* WRAP_PROMPT_STRING = COLOUR_GREY_BOLD " |" COLOUR_RESET " "; static constexpr const char* CONTINUATION_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + void start() { printf("flax repl -- version %s\n", frontend::getVersion().c_str()); @@ -34,10 +35,55 @@ namespace repl std::string input; + #if 0 + auto st = replxx::Replxx(); + st.bind_key(replxx::Replxx::KEY::ENTER, [&st, &input](char32_t code) -> replxx::Replxx::ACTION_RESULT { + // try to process the current input. + using AR = replxx::Replxx::ACTION_RESULT; + + auto inp = std::string(st.get_state().text()) + "\n"; + + if(input.empty()) + { + if(inp.empty()) + { + return AR::RETURN; + } + else if(inp[0] == ':') + { + runCommand(inp.substr(1)); + printf("\n"); + + return AR::RETURN; + } + } + + input += inp; + if(bool more = processLine(input); more) + { + // get more... + st.print("\n"); + st.print(CONTINUATION_PROMPT_STRING); + return AR::CONTINUE; + } + else + { + // ok done. + return AR::RETURN; + } + }); + + while(auto line = st.input(PROMPT_STRING)) + { + // ok, we're done -- clear. + input.clear(); + } + + #else + auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); st.setWrappedPrompt(WRAP_PROMPT_STRING); - st.setContinuationPrompt(CONTINUATION_PROMPT_STRING); while(auto line = st.read()) { @@ -68,6 +114,7 @@ namespace repl // ok, we're done -- clear. input.clear(); } + #endif } } From dd08c2a04cce348cc0774c41d041a962dd12200c Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 11 Nov 2019 22:03:03 +0800 Subject: [PATCH 063/129] like 80% of the way there???? --- source/include/ztmu.h | 250 ++++++++++++++++++++++++++++++----------- source/repl/driver.cpp | 3 +- 2 files changed, 183 insertions(+), 70 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index f7a8d9e5..79d7970b 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -24,8 +24,10 @@ namespace ztmu void clear(); std::optional read(); + std::optional readContinuation(); void setPrompt(const std::string& prompt); + void setContPrompt(const std::string& prompt); void setWrappedPrompt(const std::string& prompt); void move_cursor_left(int n); @@ -43,13 +45,19 @@ namespace ztmu void setEnterHandler(std::function handler); std::string promptString; + std::string contPromptString; std::string wrappedPromptString; + size_t normPL = 0; + size_t contPL = 0; + size_t wrapPL = 0; + size_t cursor = 0; size_t byteCursor = 0; size_t lineIdx = 0; size_t wrappedLineIdx = 0; + size_t cachedNWLForCurrentLine = 0; size_t termWidth = 0; size_t termHeight = 0; @@ -259,6 +267,18 @@ namespace detail #endif + #if 1 + template + void ztmu_dbg(const char* fmt, Args&&... args) + { + fprintf(stderr, "%s", zpr::sprint(fmt, args...).c_str()); + } + #else + template + void ztmu_dbg(const char* fmt, Args&&... args) { } + #endif + + @@ -745,13 +765,10 @@ namespace detail - static void _refresh_line(State* st, size_t lineIdx, std::string* oldLine = 0) + // returns (linesBelowCursor, finalHcursor) + static std::pair _refresh_line(State* st, size_t lineIdx, bool skip_cursor, const std::string& oldLine, + bool defer_flush, std::string* commandBuffer) { - auto normPL = displayedTextLength(st->promptString); - auto contPL = displayedTextLength(st->contPromptString); - auto wrapPL = displayedTextLength(st->wrappedPromptString); - - std::string commands; auto qcmd = [&commands](const std::string_view& c) { commands += c; @@ -769,7 +786,7 @@ namespace detail size_t lines = 1; while(len > 0) { - auto left = width - (lines == 1 ? normPL : wrapPL); + auto left = width - (lines == 1 ? st->normPL : st->wrapPL); if(left == len) lines += 1; @@ -792,7 +809,7 @@ namespace detail while(true) { - auto left = st->termWidth - (lc == 1 ? normPL : wrapPL); + auto left = st->termWidth - (lc == 1 ? st->normPL : st->wrapPL); auto todo = std::min(left, remaining); if(remaining == left) @@ -818,30 +835,34 @@ namespace detail auto getPromptForLine = [&](size_t wli) -> std::pair { if(lineIdx == 0) { - if(wli == 0) return { st->promptString, normPL }; - else return { st->wrappedPromptString, wrapPL }; + if(wli == 0) return { st->promptString, st->normPL }; + else return { st->wrappedPromptString, st->wrapPL }; } else { - if(wli == 0) return { st->contPromptString, contPL }; - else return { st->wrappedPromptString, wrapPL }; + if(wli == 0) return { st->contPromptString, st->contPL }; + else return { st->wrappedPromptString, st->wrapPL }; } }; - if(!oldLine) - oldLine = &st->lines[lineIdx]; - std::string_view currentLine = st->lines[lineIdx]; size_t numWrappingLines = calc_wrap(displayedTextLength(currentLine)); - size_t old_NWL = calc_wrap(displayedTextLength(*oldLine)); + + // if we're operating on the current line, shun bian cache the NWL count. + // we'll use this for cursor_down among other things. + if(st->lineIdx == lineIdx) + st->cachedNWLForCurrentLine = numWrappingLines; + + + size_t old_NWL = calc_wrap(displayedTextLength(oldLine)); auto new_wli = calc_wli(st->cursor); qcmd(moveCursorLeft(9999)); - fprintf(stderr, "nwl: %zu, old_nwl: %zu, new_wli: %zu, old_wli: %zu\n", numWrappingLines, old_NWL, new_wli, st->wrappedLineIdx); + ztmu_dbg("nwl: %zu, old_nwl: %zu, new_wli: %zu, old_wli: %zu\n", numWrappingLines, old_NWL, new_wli, st->wrappedLineIdx); // what we want to do here is go all the way to the bottom of the string (regardless of cursor position), and clear it. // then go up one line and repeat, basically clearing the whole thing. we don't clear lines above the cursor! @@ -854,10 +875,10 @@ namespace detail auto vcursor = getCursorPosition().y; auto tomove = nwl - st->wrappedLineIdx - 1; - // fprintf(stderr, "height: %zu, vc: %zu, tm: %zu\n", st->termHeight, vcursor, tomove); + // ztmu_dbg("height: %zu, vc: %zu, tm: %zu\n", st->termHeight, vcursor, tomove); if(st->termHeight - vcursor < tomove) { - fprintf(stderr, "need to scroll\n"); + ztmu_dbg("need to scroll\n"); // scroll the screen down -- but move the text *up* qcmd(zpr::sprint("%s%dS", CSI, tomove - (st->termHeight - vcursor))); } @@ -883,8 +904,8 @@ namespace detail size_t alrPrinted = 0; // TODO: we always use normPL here, but we should choose between normPL and contPL! - auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, normPL, wrapPL, &didWrap); - fprintf(stderr, "didWrap: %d\n", didWrap); + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, st->normPL, st->wrapPL, &didWrap); + ztmu_dbg("didWrap: %d\n", didWrap); if(didWrap) { @@ -908,32 +929,26 @@ namespace detail qcmd(moveCursorLeft(9999)); qcmd(st->wrappedPromptString); - alrPrinted = wrapPL; + alrPrinted = st->wrapPL; } else { auto [ pstr, plen ] = getPromptForLine(new_wli); - fprintf(stderr, "aaa\n"); auto leftCPs = hcursor - plen; auto leftByteCursor = calculate_left_codepoints(leftCPs, currentLine, st->cursor, st->byteCursor).second; - fprintf(stderr, "bbb\n"); // we need to print leftCPs of stuff *before* the current cursor. auto leftPortion = currentLine.substr(leftByteCursor, st->byteCursor - leftByteCursor); - fprintf(stderr, "ccc\n"); - fprintf(stderr, "ddd li=%zu, (%zu, %zu) - '%s'\n", lineIdx, st->byteCursor, currentLine.size(), - std::string(currentLine).c_str()); - currentLine.remove_prefix(st->byteCursor); auto rem = displayedTextLength(currentLine); - fprintf(stderr, "hcursor = %zu, remaining = %zu, bc = %zu, lbc = %zu, len = %zu, line = '%.*s', pr = '%s', plen = %zu\n", + ztmu_dbg("hcursor = %zu, remaining = %zu, bc = %zu, lbc = %zu, len = %zu, line = '%.*s', pr = '%s', plen = %zu\n", hcursor, rem, st->byteCursor, leftByteCursor, currentLine.length(), static_cast(currentLine.length()), currentLine.data(), pstr.c_str(), plen); - fprintf(stderr, "leftportion = '%s'\n", std::string(leftPortion).c_str()); + ztmu_dbg("leftportion = '%s'\n", std::string(leftPortion).c_str()); qcmd(pstr); qcmd(leftPortion); @@ -967,7 +982,7 @@ namespace detail return ret; }; - auto left = st->termWidth - (printedLines == 0 ? alrPrinted : wrapPL); + auto left = st->termWidth - (printedLines == 0 ? alrPrinted : st->wrapPL); auto todo = std::min(left, remaining); qcmd(adv_and_cons(todo)); @@ -979,7 +994,7 @@ namespace detail if(todo != left) qcmd(zpr::sprint("%s0K", CSI)); - fprintf(stderr, "broke\n"); + ztmu_dbg("broke\n"); break; } @@ -993,31 +1008,47 @@ namespace detail printedLines += 1; } - qcmd(moveCursorLeft(9999)); + if(!skip_cursor) + { + // ok time to move up... we know that we padded with spaces all the way to the edge + // so we can move up by printedLines, and left by width - hcursor + qcmd(moveCursorUp(static_cast(printedLines))); + qcmd(moveCursorLeft(9999)); + qcmd(moveCursorRight(hcursor)); + } + + ztmu_dbg("hcursor = %zu\n", hcursor); - // ok time to move up... we know that we padded with spaces all the way to the edge - // so we can move up by printedLines, and left by width - hcursor - qcmd(moveCursorUp(static_cast(printedLines))); - qcmd(moveCursorLeft(9999)); - qcmd(moveCursorRight(hcursor)); - fprintf(stderr, "hcursor = %zu\n", hcursor); - } + st->wrappedLineIdx = calc_wli(st->cursor); + ztmu_dbg("updated wli: %zu\n\n", st->wrappedLineIdx); - st->wrappedLineIdx = calc_wli(st->cursor); - fprintf(stderr, "updated wli: %zu\n\n", st->wrappedLineIdx); - flushcmd(); + if(defer_flush) *commandBuffer = commands; + else flushcmd(); + + return std::make_pair(printedLines, hcursor); + } } static void refresh_line(State* st, std::string* oldLine = 0) { // refresh the top line manually: - _refresh_line(st, st->lineIdx, oldLine); + + ztmu_dbg("\n\n** refreshing line 0...\n"); + auto [ down, hcursor ] = _refresh_line(st, st->lineIdx, /* skip_cursor: */ false, + oldLine ? *oldLine : st->lines[st->lineIdx], /* defer_flush: */ false, /* cmd_buffer: */ nullptr); + + down += 1; + ztmu_dbg("down = %zu\n", down); // this is not re-entrant!!! auto old_wli = st->wrappedLineIdx; auto old_bcr = st->byteCursor; auto old_cur = st->cursor; + size_t downAccum = 0; + + std::string buffer; + // refresh every line including and after the current line. for(size_t i = st->lineIdx + 1; i < st->lines.size(); i++) { @@ -1025,8 +1056,24 @@ namespace detail st->byteCursor = 0; st->cursor = 0; - fprintf(stderr, "** refreshing line %zu...\n", i); - _refresh_line(st, i, &st->lines[i]); + buffer += moveCursorDown(down); + downAccum += down; + + ztmu_dbg("** refreshing line %zu...\n", i); + + std::string buf; + down = _refresh_line(st, i, /* skip_cursor: */ true, st->lines[i], /* defer_flush: */ true, + &buf).first - 1; + + buffer += buf; + + ztmu_dbg("down = %zu\n", down); + } + + if(downAccum > 0) + { + platform_write(zpr::sprint("%s%s%s%s", buffer, moveCursorUp(downAccum), moveCursorLeft(9999), + moveCursorRight(hcursor))); } st->wrappedLineIdx = old_wli; @@ -1034,6 +1081,8 @@ namespace detail st->cursor = old_cur; } + + static void cursor_home(State* st) { st->cursor = 0; @@ -1122,15 +1171,14 @@ namespace detail { if(st->cursor > 0 && st->wrappedLineIdx > 0) { - size_t promptL = displayedTextLength(st->lineIdx == 0 ? st->promptString : st->contPromptString); - size_t wrapL = displayedTextLength(st->wrappedPromptString); + size_t promptL = st->lineIdx == 0 ? st->normPL : st->contPL; // first, get the current horz cursor position: - auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, wrapL); + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, st->wrapPL); // because wrappedLineIdx > 0, we know we're wrapping. this is the number of chars on the current // line (of text!), to the left of the cursor. - auto h_curlength = hcursor - wrapL; + auto h_curlength = hcursor - st->wrapPL; // this is how many extra chars to go left. auto rightmargin = st->termWidth - hcursor; @@ -1156,26 +1204,24 @@ namespace detail else if(st->lineIdx > 0) { // ok -- we are now into weird strange territory. move up into the previous continuation line... - fprintf(stderr, "going up...\n"); + ztmu_dbg("going up...\n"); std::string_view prevLine = st->lines[st->lineIdx - 1]; size_t prevLineLen = displayedTextLength(prevLine); - size_t promptL = displayedTextLength(st->contPromptString); - size_t wrapL = displayedTextLength(st->wrappedPromptString); - - // first, get the current horz cursor position: - auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, wrapL); + // first, get the current horz cursor position. for the current line, we must always be + // in a continuation if are able to go upwards. + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, st->contPL, st->wrapPL); // now, we get the offset of the previous line: auto prev_hcursor = get_cursor_line_offset(st->termWidth, prevLineLen, - st->lineIdx > 1 ? promptL : displayedTextLength(st->promptString), wrapL); + st->lineIdx > 1 ? st->contPL : st->normPL, st->wrapPL); auto prev_right_margin = st->termWidth - prev_hcursor; st->cursor = prevLineLen; st->byteCursor = convertCursorToByteCursor(reinterpret_cast(prevLine.data()), st->cursor); - fprintf(stderr, "c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, + ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, prev_right_margin, std::string(prevLine).c_str()); st->lineIdx -= 1; @@ -1183,9 +1229,9 @@ namespace detail // and then we move the cursor. platform_write(moveCursorUp(1)); cursor_left(st, st->termWidth - hcursor - prev_right_margin, /* refresh: */ false); - _refresh_line(st, st->lineIdx); + refresh_line(st); - fprintf(stderr, "c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, + ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, prev_right_margin, std::string(prevLine).c_str()); } else @@ -1196,15 +1242,12 @@ namespace detail static void cursor_down(State* st) { - if(st->byteCursor < getCurLine(st).size()) + if(st->byteCursor < getCurLine(st).size() && (st->lines.size() == 1 || st->wrappedLineIdx + 1 < st->cachedNWLForCurrentLine)) { // works on a similar principle as cursor_up. - // TODO: - size_t promptL = displayedTextLength(st->lineIdx == 0 ? st->promptString : st->contPromptString); - size_t wrapL = displayedTextLength(st->wrappedPromptString); - - auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, promptL, wrapL); + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, + st->lineIdx == 0 ? st->normPL : st->contPL, st->wrapPL); // again, we make a similar assumption -- that only the first and second prompts can differ; the second and // subsequent wrapping prompts must have the same length. @@ -1212,10 +1255,62 @@ namespace detail auto rightmargin = st->termWidth - hcursor; // in the next line, the prompt will always be a wrapping prompt. - auto leftmargin = hcursor - wrapL; + auto leftmargin = hcursor - st->wrapPL; cursor_right(st, rightmargin + leftmargin); } + else if(st->lineIdx + 1 < st->lines.size()) + { + ztmu_dbg("going down...\n"); + + std::string_view nextLine = st->lines[st->lineIdx + 1]; + size_t nextLineLen = displayedTextLength(nextLine); + + // first, get the current horz cursor position: + auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, + st->lineIdx == 0 ? st->normPL: st->contPL, st->wrapPL); + + // auto leftChars = hcursor - (st->wrappedLineIdx > 0 ? st->wrapPL : (st->lineIdx == 0 ? st->normPL : st->contPL)); + + // ok, the next line will definitely be a continuation prompt. so, see how many chars into the line + // we'll actually put ourselves -- and handle the edge case of negative values! + auto next_leftChars = (hcursor < st->contPL + ? 0 // snap to the beginning of the line, then. + : std::min(hcursor - st->contPL, nextLineLen) + ); + + // ok, time to do the real work. + st->cursor = next_leftChars; + st->byteCursor = convertCursorToByteCursor(reinterpret_cast(nextLine.data()), st->cursor); + st->lineIdx += 1; + + // now move the cursor and refresh! + platform_write(moveCursorDown(1)); + refresh_line(st); + + + + // // now, we get the offset of the previous line: + // auto prev_hcursor = get_cursor_line_offset(st->termWidth, prevLineLen, + // st->lineIdx > 1 ? promptL : displayedTextLength(st->promptString), wrapL); + + // auto prev_right_margin = st->termWidth - prev_hcursor; + + // st->cursor = prevLineLen; + // st->byteCursor = convertCursorToByteCursor(reinterpret_cast(prevLine.data()), st->cursor); + // ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, + // prev_right_margin, std::string(prevLine).c_str()); + + // st->lineIdx -= 1; + + // // and then we move the cursor. + // platform_write(moveCursorUp(1)); + // cursor_left(st, st->termWidth - hcursor - prev_right_margin, /* refresh: */ false); + // refresh_line(st); + + // ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, + // prev_right_margin, std::string(prevLine).c_str()); + } else { // TODO: handle moving down into the next "line" list -- during continuations. @@ -1281,7 +1376,7 @@ namespace detail if(platform_read_one(&c) <= 0) break; - // fprintf(stderr, "[%d]", c); + // ztmu_dbg("[%d]", c); switch(c) { // enter @@ -1492,11 +1587,19 @@ namespace ztmu void State::setPrompt(const std::string& prompt) { this->promptString = prompt; + this->normPL = detail::displayedTextLength(prompt); + } + + void State::setContPrompt(const std::string& prompt) + { + this->contPromptString = prompt; + this->contPL = detail::displayedTextLength(prompt); } void State::setWrappedPrompt(const std::string& prompt) { this->wrappedPromptString = prompt; + this->wrapPL = detail::displayedTextLength(prompt); } void State::move_cursor_left(int n) @@ -1570,6 +1673,17 @@ namespace ztmu if(eof) return std::nullopt; else return this->lines.back(); } + + std::optional State::readContinuation() + { + // don't clear + this->lineIdx++; + + bool eof = detail::read_line(this, /* promptMode: */ 1); + + if(eof) return std::nullopt; + else return this->lines.back(); + } } diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index c8324261..8c82d408 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -84,6 +84,7 @@ namespace repl auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); st.setWrappedPrompt(WRAP_PROMPT_STRING); + st.setContPrompt(CONTINUATION_PROMPT_STRING); while(auto line = st.read()) { @@ -118,8 +119,6 @@ namespace repl } } -// std::wcerr << L""; - From 506105aa2bceb7bcd2171a0d66d32b44015a3a48 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 11 Nov 2019 23:43:46 +0800 Subject: [PATCH 064/129] repl works!!!! --- source/codegen/autocasting.cpp | 7 + source/fir/ConstantValue.cpp | 109 +++++++++- source/fir/GlobalValue.cpp | 11 + source/frontend/file.cpp | 21 +- source/frontend/parser/function.cpp | 7 +- source/frontend/parser/type.cpp | 2 +- source/include/ir/constant.h | 31 ++- source/include/parser_internal.h | 6 +- source/include/ztmu.h | 322 +++++++++++----------------- source/repl/driver.cpp | 74 ++----- source/repl/parse.cpp | 70 +++++- 11 files changed, 388 insertions(+), 272 deletions(-) diff --git a/source/codegen/autocasting.cpp b/source/codegen/autocasting.cpp index 85dedef6..56026977 100644 --- a/source/codegen/autocasting.cpp +++ b/source/codegen/autocasting.cpp @@ -326,6 +326,13 @@ namespace cgn } } + // TODO: do we really want this? + else if((lt->isFloatingPointType() && rt->isIntegerType()) || (rt->isFloatingPointType() && lt->isIntegerType())) + { + if(lt->isFloatingPointType()) return { lhs, this->irb.IntToFloatCast(rhs, lt) }; + else return { this->irb.IntToFloatCast(lhs, rt), rhs }; + } + // nope... warn(this->loc(), "unsupported autocast of '%s' -> '%s'", lt, rt); return { 0, 0 }; diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 223fa890..cb9cdb35 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -2,13 +2,15 @@ // Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. -#include "ir/value.h" -#include "ir/constant.h" +#include +#include +#include -#include -#include #include +#include "ir/value.h" +#include "ir/constant.h" + namespace fir { ConstantValue::ConstantValue(Type* t) : Value(t) @@ -29,6 +31,11 @@ namespace fir return ret; } + std::string ConstantValue::str() + { + return ""; + } + ConstantBool* ConstantBool::get(bool val) { @@ -44,6 +51,15 @@ namespace fir return this->value; } + std::string ConstantBool::str() + { + return this->value ? "true" : "false"; + } + + + + + ConstantBitcast* ConstantBitcast::get(ConstantValue* v, Type* t) { @@ -54,6 +70,11 @@ namespace fir { } + std::string ConstantBitcast::str() + { + return this->value->str(); + } + @@ -67,6 +88,13 @@ namespace fir this->number = n; } + std::string ConstantNumber::str() + { + // 6 decimal places, like default printf. + return this->number.toString("%.6R"); + } + + // todo: unique these values. ConstantInt* ConstantInt::get(Type* intType, uint64_t val) @@ -145,6 +173,21 @@ namespace fir return ConstantInt::get(Type::getNativeUWord(), value); } + std::string ConstantInt::str() + { + char buf[64] = {0}; + if(this->getType() == Type::getInt8()) snprintf(buf, 63, "%" PRIi8, static_cast(this->value)); + else if(this->getType() == Type::getInt16()) snprintf(buf, 63, "%" PRIi16, static_cast(this->value)); + else if(this->getType() == Type::getInt32()) snprintf(buf, 63, "%" PRIi32, static_cast(this->value)); + else if(this->getType() == Type::getInt64()) snprintf(buf, 63, "%" PRIi64, static_cast(this->value)); + else if(this->getType() == Type::getUint8()) snprintf(buf, 63, "%" PRIu8, static_cast(this->value)); + else if(this->getType() == Type::getUint16()) snprintf(buf, 63, "%" PRIu16, static_cast(this->value)); + else if(this->getType() == Type::getUint32()) snprintf(buf, 63, "%" PRIu32, static_cast(this->value)); + else if(this->getType() == Type::getUint64()) snprintf(buf, 63, "%" PRIu64, static_cast(this->value)); + else snprintf(buf, 63, ""); + + return std::string(buf); + } @@ -210,6 +253,17 @@ namespace fir return ConstantFP::get(Type::getFloat64(), value); } + std::string ConstantFP::str() + { + char buf[64] = {0}; + if(this->getType() == Type::getFloat32()) snprintf(buf, 63, "%.6f", static_cast(this->value)); + else if(this->getType() == Type::getFloat64()) snprintf(buf, 63, "%.6f", static_cast(this->value)); + else snprintf(buf, 63, ""); + + return std::string(buf); + } + + @@ -237,6 +291,14 @@ namespace fir this->members = members; } + std::string ConstantStruct::str() + { + std::string ret = this->getType()->str() + " {\n"; + for(auto x : this->members) + ret += " " + x->str() + "\n"; + + return ret + "}"; + } @@ -250,16 +312,23 @@ namespace fir ConstantCharSlice::ConstantCharSlice(const std::string& s) : ConstantValue(fir::Type::getCharSlice(false)) { - this->str = s; + this->value = s; } std::string ConstantCharSlice::getValue() { - return this->str; + return this->value; + } + + std::string ConstantCharSlice::str() + { + return this->value; } + + ConstantTuple* ConstantTuple::get(const std::vector& mems) { return new ConstantTuple(mems); @@ -287,6 +356,12 @@ namespace fir this->values = mems; } + std::string ConstantTuple::str() + { + return "(" + util::listToString(this->values, [](auto x) -> auto { return x->str(); }) + ")"; + } + + @@ -317,6 +392,12 @@ namespace fir this->value = value; } + std::string ConstantEnumCase::str() + { + // TODO: why the fuck did i design enums this way?! + return this->value->str(); + } + @@ -350,6 +431,11 @@ namespace fir this->values = vals; } + std::string ConstantArray::str() + { + return "[ " + util::listToString(this->values, [](auto x) -> auto { return x->str(); }) + " ]"; + } + @@ -377,6 +463,12 @@ namespace fir { } + std::string ConstantDynamicArray::str() + { + return ""; + } + + @@ -393,6 +485,11 @@ namespace fir ConstantArraySlice::ConstantArraySlice(ArraySliceType* t) : ConstantValue(t) { } + + std::string ConstantArraySlice::str() + { + return ""; + } } diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index 419b7b7c..907921a6 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -20,6 +20,12 @@ namespace fir else this->kind = Kind::prvalue; } + std::string GlobalValue::str() + { + return ""; + } + + GlobalVariable::GlobalVariable(const Identifier& name, Module* module, Type* type, bool immutable, LinkageType lt, ConstantValue* initValue) : GlobalValue(module, type, lt, !immutable) @@ -43,6 +49,11 @@ namespace fir { return this->initValue; } + + std::string GlobalVariable::str() + { + return this->initValue->str(); + } } diff --git a/source/frontend/file.cpp b/source/frontend/file.cpp index 965bba85..471a4d26 100644 --- a/source/frontend/file.cpp +++ b/source/frontend/file.cpp @@ -69,8 +69,27 @@ namespace frontend size_t i = 0; do { + // store it here so we can fiddle with it later, if we need to. + auto tok_out = ts->getNextSlotAndIncrement(); + auto type = lexer::getNextToken(lines, &curLine, &curOffset, fileContents, *pos, - ts->getNextSlotAndIncrement(), crlf); + tok_out, crlf); + + // if we reached the end of file, do everybody a favour and insert a newline before the + // EOF token. the lexer itself can't do this, because it can only operate on one token + // at a time! + + if(type == lexer::TokenType::EndOfFile) + { + // here is the aforementioned fiddling. + tok_out->type = lexer::TokenType::NewLine; + tok_out->text = "\n"; + + // ok, now we can make another one. + auto real_eof = ts->getNextSlotAndIncrement(); + real_eof->loc = tok_out->loc; + real_eof->type = lexer::TokenType::EndOfFile; + } flag = (type != lexer::TokenType::EndOfFile); diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index 99a74a93..db90c8a8 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -139,7 +139,7 @@ namespace parser - FuncDefn* parseFunction(State& st) + PResult parseFunction(State& st) { auto [ defn, isvar, varloc ] = parseFunctionDecl(st); if(isvar) @@ -151,7 +151,10 @@ namespace parser st.enterFunctionBody(); { - defn->body = parseBracedBlock(st).val(); + auto body = parseBracedBlock(st); + + if(body.hasValue()) defn->body = body.val(); + else return PResult::copyError(body); } st.leaveFunctionBody(); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index d51e79b9..a813d1e0 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -265,7 +265,7 @@ namespace parser else if(st.front() == TT::Func) { // ok parse a func as usual - auto method = parseFunction(st); + auto method = parseFunction(st).val(); addSelfToMethod(method, method->isMutating); defn->methods.push_back(method); diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 30e05a59..979fc917 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -26,6 +26,7 @@ namespace fir static ConstantValue* getZeroValue(Type* type); static ConstantValue* getNull(); + virtual std::string str(); protected: ConstantValue(Type* type); @@ -49,6 +50,8 @@ namespace fir float getFloat() { return this->number.toFloat(); } double getDouble() { return this->number.toDouble(); } + virtual std::string str() override; + protected: ConstantNumber(ConstantNumberType* cnt, const mpfr::mpreal& n); @@ -62,6 +65,8 @@ namespace fir static ConstantBool* get(bool value); bool getValue(); + virtual std::string str() override; + protected: ConstantBool(bool val); @@ -89,6 +94,8 @@ namespace fir int64_t getSignedValue(); uint64_t getUnsignedValue(); + virtual std::string str() override; + protected: ConstantInt(Type* type, int64_t val); ConstantInt(Type* type, uint64_t val); @@ -109,6 +116,8 @@ namespace fir double getValue(); + virtual std::string str() override; + protected: ConstantFP(Type* type, float val); ConstantFP(Type* type, double val); @@ -124,6 +133,8 @@ namespace fir std::vector getValues() { return this->values; } + virtual std::string str() override; + protected: ConstantArray(Type* type, const std::vector& vals); @@ -136,6 +147,8 @@ namespace fir static ConstantStruct* get(StructType* st, const std::vector& members); + virtual std::string str() override; + protected: ConstantStruct(StructType* st, const std::vector& members); std::vector members; @@ -150,6 +163,8 @@ namespace fir ConstantInt* getIndex(); ConstantValue* getValue(); + virtual std::string str() override; + protected: ConstantEnumCase(EnumType* en, ConstantInt* index, ConstantValue* value); @@ -164,10 +179,12 @@ namespace fir static ConstantCharSlice* get(const std::string& value); std::string getValue(); + virtual std::string str() override; + protected: ConstantCharSlice(const std::string& str); - std::string str; + std::string value; }; struct ConstantTuple : ConstantValue @@ -177,6 +194,8 @@ namespace fir static ConstantTuple* get(const std::vector& mems); std::vector getValues(); + virtual std::string str() override; + protected: ConstantTuple(const std::vector& mems); std::vector values; @@ -195,6 +214,8 @@ namespace fir ConstantArray* getArray() { return this->arr; } + virtual std::string str() override; + protected: ConstantDynamicArray(DynamicArrayType* type); @@ -214,6 +235,8 @@ namespace fir ConstantValue* getData() { return this->data; } ConstantValue* getLength() { return this->length; } + virtual std::string str() override; + protected: ConstantArraySlice(ArraySliceType* type); @@ -229,6 +252,8 @@ namespace fir ConstantValue* getValue() { return this->value; } + virtual std::string str() override; + protected: ConstantBitcast(ConstantValue* v, Type* target); @@ -245,6 +270,8 @@ namespace fir Module* getParentModule() { return this->parentModule; } + virtual std::string str() override; + protected: GlobalValue(Module* mod, Type* type, LinkageType linkage, bool mut = false); @@ -259,6 +286,8 @@ namespace fir void setInitialValue(ConstantValue* constVal); ConstantValue* getInitialValue(); + virtual std::string str() override; + protected: ConstantValue* initValue = 0; }; diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 44cb21ca..c439e2ac 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -337,14 +337,14 @@ namespace parser template >> PResult map(const F& fn) const { - if(this->state > 0) return PResult(this->error); + if(this->state > 0) return PResult(this->error, this->state); else return PResult(fn(this->value)); } template ::value_t> PResult flatmap(const F& fn) const { - if(this->state > 0) return PResult(this->error); + if(this->state > 0) return PResult(this->error, this->state); else return fn(this->value); } @@ -439,7 +439,7 @@ namespace parser ast::Stmt* parseVariable(State& st); ast::ReturnStmt* parseReturn(State& st); ast::ImportStmt* parseImport(State& st); - ast::FuncDefn* parseFunction(State& st); + PResult parseFunction(State& st); PResult parseStmtWithAccessSpec(State& st); ast::ForeignFuncDefn* parseForeignFunction(State& st); ast::OperatorOverloadDefn* parseOperatorOverload(State& st); diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 79d7970b..8076681e 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -19,12 +20,39 @@ namespace ztmu { + enum class Key + { + // start above 0xFF, so anything below we can just use as normal chars. + ESCAPE = 0x100, + ENTER, + UP, + DOWN, + LEFT, + RIGHT, + HOME, + END, + DELETE, + INSERT, + PAGEUP, + PAGEDOWN, + F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, + F10, F11, F12, F13, F14, F15, F16, F17, + F18, F19, F20, F21, F22, F23, F24 + }; + + enum class HandlerAction + { + CONTINUE, // do nothing, and continue reading the line. + RETURN, // as if enter was pressed -- finish the line and give it back. + QUIT // abandon ship -- return EOF. + }; + struct State { void clear(); std::optional read(); - std::optional readContinuation(); + std::optional readContinuation(const std::string& seed = ""); void setPrompt(const std::string& prompt); void setContPrompt(const std::string& prompt); @@ -42,7 +70,12 @@ namespace ztmu void delete_left(int n); void delete_right(int n); - void setEnterHandler(std::function handler); + // the handler will return true to continue reading, false to stop and return all input. + void setKeyHandler(Key k, std::function handler); + void unsetKeyHandler(Key k); + + std::string getCurrentLine(); + void setCurrentLine(const std::string& s); std::string promptString; std::string contPromptString; @@ -63,7 +96,7 @@ namespace ztmu size_t termHeight = 0; std::vector lines; - std::function enterHandler; + std::map> keyHandlers; }; } @@ -80,25 +113,6 @@ namespace detail int y = -1; }; - enum class Key - { - NONE = 0, - ESCAPE, - UP, - DOWN, - LEFT, - RIGHT, - HOME, - END, - DELETE, - INSERT, - PAGEUP, - PAGEDOWN, - F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, - F10, F11, F12, F13, F14, F15, F16, F17, - F18, F19, F20, F21, F22, F23, F24 - }; - struct ControlSeq { ControlSeq() { } @@ -111,73 +125,6 @@ namespace detail int params[8]; }; - struct KeyEvent - { - union { - char key; - Key escKey; - ControlSeq ctrlSeq; - }; - - KeyEvent() - { - this->escKey = Key::NONE; - this->isNormalChar = false; - this->isControlSeq = false; - } - - KeyEvent(const ControlSeq& cs) - { - this->ctrlSeq = cs; - this->isNormalChar = false; - this->isControlSeq = true; - } - - KeyEvent(Key escKey) - { - this->escKey = escKey; - this->isNormalChar = false; - this->isControlSeq = false; - } - - KeyEvent(char c) - { - this->key = c; - this->isNormalChar = true; - this->isControlSeq = false; - } - - KeyEvent ctrl() - { - auto copy = *this; - copy.isControlled = true; - return copy; - } - - KeyEvent alt() - { - auto copy = *this; - copy.isAlted = true; - return copy; - } - - KeyEvent shift() - { - auto copy = *this; - copy.isShifted = true; - return copy; - } - - - bool isNormalChar = true; - bool isControlSeq = false; - - bool isAlted = false; - bool isShifted = false; - bool isControlled = false; - }; - - static void leaveRawMode(); static void enterRawMode(); @@ -194,7 +141,7 @@ namespace detail static std::string setCursorPosition(const CursorPosition& pos); static size_t displayedTextLength(const std::string_view& str); - static std::pair parseEscapeSequence(const std::string_view& str); + static size_t parseEscapeSequence(const std::string_view& str); @@ -267,7 +214,7 @@ namespace detail #endif - #if 1 + #if 0 template void ztmu_dbg(const char* fmt, Args&&... args) { @@ -367,10 +314,10 @@ namespace detail - static std::pair parseEscapeSequence(const std::string_view& str) + static size_t parseEscapeSequence(const std::string_view& str) { if(str.size() < 2 || str[0] != ESC) - return std::make_pair(KeyEvent(), 0); + return 0; auto read_digits = [](const std::string_view& s, int* out) -> size_t { int ret = 0; @@ -393,11 +340,11 @@ namespace detail if(str[1] == ESC) { - return std::make_pair(KeyEvent(Key::ESCAPE), 2); + return 2; } else if(str[1] == 'N' || str[1] == 'O' || str[1] == 'c') { - return std::make_pair(KeyEvent(ControlSeq(str[1])), 2); + return 2; } else if(str[1] == 'P' || str[1] == ']' || str[1] == 'X' || str[1] == '^' || str[1] == '_') { @@ -406,7 +353,7 @@ namespace detail while(cons + 1 < str.size() && (str[cons] != ESC || str[cons + 1] != '\\')) cons++; - return std::make_pair(ControlSeq(str[1]), cons); + return cons; } else if(str[1] == '[') { @@ -440,75 +387,11 @@ namespace detail } csi.lastChar = str[cons++]; - switch(csi.lastChar) - { - case '~': { - auto ke = KeyEvent(); - switch(csi.params[0]) - { - case 1: ke = KeyEvent(Key::HOME); break; - case 2: ke = KeyEvent(Key::INSERT); break; - case 3: ke = KeyEvent(Key::DELETE); break; - case 4: ke = KeyEvent(Key::END); break; - case 5: ke = KeyEvent(Key::PAGEUP); break; - case 6: ke = KeyEvent(Key::PAGEDOWN); break; - case 7: ke = KeyEvent(Key::HOME); break; - case 8: ke = KeyEvent(Key::END); break; - case 10: ke = KeyEvent(Key::F0); break; - case 11: ke = KeyEvent(Key::F1); break; - case 12: ke = KeyEvent(Key::F2); break; - case 13: ke = KeyEvent(Key::F3); break; - case 14: ke = KeyEvent(Key::F4); break; - case 15: ke = KeyEvent(Key::F5); break; - case 17: ke = KeyEvent(Key::F6); break; - case 18: ke = KeyEvent(Key::F7); break; - case 19: ke = KeyEvent(Key::F8); break; - case 20: ke = KeyEvent(Key::F9); break; - case 21: ke = KeyEvent(Key::F10); break; - case 23: ke = KeyEvent(Key::F11); break; - case 24: ke = KeyEvent(Key::F12); break; - case 25: ke = KeyEvent(Key::F13); break; - case 26: ke = KeyEvent(Key::F14); break; - case 28: ke = KeyEvent(Key::F15); break; - case 29: ke = KeyEvent(Key::F16); break; - case 31: ke = KeyEvent(Key::F17); break; - case 32: ke = KeyEvent(Key::F18); break; - case 33: ke = KeyEvent(Key::F19); break; - case 34: ke = KeyEvent(Key::F20); break; - } - - if(ke.escKey != Key::NONE && csi.numParams == 2) - { - int mods = csi.params[1] - 1; - if(mods & 0x1) ke = ke.shift(); - if(mods & 0x2) ke = ke.alt(); - if(mods & 0x4) ke = ke.ctrl(); - if(mods & 0x8) ke = ke.alt(); // actually meta - } - - return std::make_pair(ke, cons); - } - - case 'A': return std::make_pair(KeyEvent(Key::UP), cons); - case 'B': return std::make_pair(KeyEvent(Key::DOWN), cons); - case 'C': return std::make_pair(KeyEvent(Key::RIGHT), cons); - case 'D': return std::make_pair(KeyEvent(Key::LEFT), cons); - case 'F': return std::make_pair(KeyEvent(Key::END), cons); - case 'H': return std::make_pair(KeyEvent(Key::HOME), cons); - - case 'P': return std::make_pair(KeyEvent(Key::F1), cons); - case 'Q': return std::make_pair(KeyEvent(Key::F2), cons); - case 'R': return std::make_pair(KeyEvent(Key::F3), cons); - case 'S': return std::make_pair(KeyEvent(Key::F4), cons); - } - - - - return std::make_pair(KeyEvent(csi), cons); + return cons; } else { - return std::make_pair(KeyEvent(), 0); + return 0; } } @@ -538,7 +421,7 @@ namespace detail { if(sv[i] == ESC) { - size_t k = parseEscapeSequence(sv.substr(i)).second; + size_t k = parseEscapeSequence(sv.substr(i)); i += k; cons += k; } @@ -689,11 +572,11 @@ namespace detail return i; } - static size_t convertCursorToByteCursor(const uint8_t* bytes, size_t cursor) + static size_t convertCursorToByteCursor(const char* bytes, size_t cursor) { size_t bc = 0; for(size_t i = 0; i < cursor; i++) - bc = 1 + findEndOfUTF8CP(bytes, bc); + bc = 1 + findEndOfUTF8CP(reinterpret_cast(bytes), bc); return bc; } @@ -1220,7 +1103,7 @@ namespace detail auto prev_right_margin = st->termWidth - prev_hcursor; st->cursor = prevLineLen; - st->byteCursor = convertCursorToByteCursor(reinterpret_cast(prevLine.data()), st->cursor); + st->byteCursor = convertCursorToByteCursor(prevLine.data(), st->cursor); ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, prev_right_margin, std::string(prevLine).c_str()); @@ -1281,35 +1164,12 @@ namespace detail // ok, time to do the real work. st->cursor = next_leftChars; - st->byteCursor = convertCursorToByteCursor(reinterpret_cast(nextLine.data()), st->cursor); + st->byteCursor = convertCursorToByteCursor(nextLine.data(), st->cursor); st->lineIdx += 1; // now move the cursor and refresh! platform_write(moveCursorDown(1)); refresh_line(st); - - - - // // now, we get the offset of the previous line: - // auto prev_hcursor = get_cursor_line_offset(st->termWidth, prevLineLen, - // st->lineIdx > 1 ? promptL : displayedTextLength(st->promptString), wrapL); - - // auto prev_right_margin = st->termWidth - prev_hcursor; - - // st->cursor = prevLineLen; - // st->byteCursor = convertCursorToByteCursor(reinterpret_cast(prevLine.data()), st->cursor); - // ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, - // prev_right_margin, std::string(prevLine).c_str()); - - // st->lineIdx -= 1; - - // // and then we move the cursor. - // platform_write(moveCursorUp(1)); - // cursor_left(st, st->termWidth - hcursor - prev_right_margin, /* refresh: */ false); - // refresh_line(st); - - // ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, - // prev_right_margin, std::string(prevLine).c_str()); } else { @@ -1324,7 +1184,7 @@ namespace detail // this is ugly!!! static State* currentStateForSignal = 0; - static bool read_line(State* st, int promptMode) + static bool read_line(State* st, int promptMode, std::string seed) { constexpr char CTRL_A = '\x01'; constexpr char CTRL_C = '\x03'; @@ -1340,12 +1200,17 @@ namespace detail st->termWidth = getTerminalWidth(); st->termHeight = getTerminalHeight(); - st->cursor = 0; - st->byteCursor = 0; st->wrappedLineIdx = 0; - st->lines.emplace_back(""); + { + // TODO: fix this. probably pull out calc_wli from the refresh function. + // if your seed exceeds the terminal width, then you deserve it. + st->lines.push_back(seed); + st->cursor = seed.size(); + st->byteCursor = convertCursorToByteCursor(seed.c_str(), st->cursor); + } platform_write(promptMode == 0 ? st->promptString : st->contPromptString); + platform_write(seed); bool didSetSignalHandler = false; @@ -1372,6 +1237,9 @@ namespace detail bool eof = false; while(true) { + // lol + loop_top: + char c = 0; if(platform_read_one(&c) <= 0) break; @@ -1381,6 +1249,23 @@ namespace detail { // enter case ENTER: { + if(auto fn = st->keyHandlers[Key::ENTER]; fn) + { + auto res = fn(st, Key::ENTER); + switch(res) + { + case HandlerAction::CONTINUE: + goto loop_top; + + case HandlerAction::QUIT: + eof = true; + goto finish; + + default: + break; + } + } + goto finish; } @@ -1506,6 +1391,23 @@ namespace detail auto old = getCurLine(st); uint8_t uc = static_cast(c); + + // also: for handlers, we don't bother about giving you codepoints, so... tough luck. + // TODO. + if(auto fn = st->keyHandlers[static_cast(uc)]; fn) + { + switch(fn(st, static_cast(uc))) + { + case HandlerAction::CONTINUE: + break; + + case HandlerAction::QUIT: eof = true; [[fallthrough]]; + case HandlerAction::RETURN: [[fallthrough]]; + default: + goto finish; + } + } + if(uc >= 0x20 && uc <= 0x7F) { getCurLine(st).insert(getCurLine(st).begin() + st->byteCursor, c); @@ -1659,27 +1561,49 @@ namespace ztmu detail::delete_right(this); } - void State::setEnterHandler(std::function handler) + void State::setKeyHandler(Key k, std::function handler) { - this->enterHandler = handler; + this->keyHandlers[k] = handler; + } + + void State::unsetKeyHandler(Key k) + { + this->keyHandlers[k] = std::function(); + } + + std::string State::getCurrentLine() + { + return this->lines[this->lineIdx]; + } + + void State::setCurrentLine(const std::string& s) + { + auto old = this->lines[this->lineIdx]; + this->lines[this->lineIdx] = s; + + // we need to ensure the cursors are updated! + this->cursor = std::min(this->cursor, detail::displayedTextLength(s)); + this->byteCursor = detail::convertCursorToByteCursor(s.c_str(), this->cursor); + + detail::refresh_line(this, &old); } std::optional State::read() { // clear. this->clear(); - bool eof = detail::read_line(this, /* promptMode: */ 0); + bool eof = detail::read_line(this, /* promptMode: */ 0, ""); if(eof) return std::nullopt; else return this->lines.back(); } - std::optional State::readContinuation() + std::optional State::readContinuation(const std::string& seed) { // don't clear this->lineIdx++; - bool eof = detail::read_line(this, /* promptMode: */ 1); + bool eof = detail::read_line(this, /* promptMode: */ 1, seed); if(eof) return std::nullopt; else return this->lines.back(); diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 8c82d408..3c10f602 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -25,6 +25,7 @@ namespace repl static constexpr const char* WRAP_PROMPT_STRING = COLOUR_GREY_BOLD " |" COLOUR_RESET " "; static constexpr const char* CONTINUATION_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* EXTRA_INDENT = " "; void start() { @@ -35,57 +36,20 @@ namespace repl std::string input; - #if 0 - auto st = replxx::Replxx(); - st.bind_key(replxx::Replxx::KEY::ENTER, [&st, &input](char32_t code) -> replxx::Replxx::ACTION_RESULT { - // try to process the current input. - using AR = replxx::Replxx::ACTION_RESULT; - - auto inp = std::string(st.get_state().text()) + "\n"; - - if(input.empty()) - { - if(inp.empty()) - { - return AR::RETURN; - } - else if(inp[0] == ':') - { - runCommand(inp.substr(1)); - printf("\n"); - - return AR::RETURN; - } - } - - input += inp; - if(bool more = processLine(input); more) - { - // get more... - st.print("\n"); - st.print(CONTINUATION_PROMPT_STRING); - return AR::CONTINUE; - } - else - { - // ok done. - return AR::RETURN; - } - }); - - while(auto line = st.input(PROMPT_STRING)) - { - // ok, we're done -- clear. - input.clear(); - } - - #else - auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); st.setWrappedPrompt(WRAP_PROMPT_STRING); st.setContPrompt(CONTINUATION_PROMPT_STRING); + st.setKeyHandler(static_cast('}'), [](ztmu::State* st, ztmu::Key k) -> ztmu::HandlerAction { + // a bit dirty, but we just do this -- if we can find the indent at the back, then remove it. + auto line = st->getCurrentLine(); + if(line.size() >= 2 && line.find(EXTRA_INDENT, line.size() - strlen(EXTRA_INDENT)) != -1) + st->setCurrentLine(line.substr(0, line.size() - 2)); + + return ztmu::HandlerAction::CONTINUE; + }); + while(auto line = st.read()) { input += std::string(*line); @@ -98,13 +62,22 @@ namespace repl runCommand(input.substr(1)); printf("\n"); } - else if(bool needmore = processLine(input += "\n"); needmore) + else if(bool needmore = processLine(input); needmore) { - // read more. - while(auto line = st.readContinuation()) + const char* indent = ""; + switch(input.back()) { - input += std::string(*line) + "\n"; + case '{': [[fallthrough]]; + case '(': [[fallthrough]]; + case '[': [[fallthrough]]; + case ',': + indent = EXTRA_INDENT; + } + // read more. + while(auto line = st.readContinuation(indent)) + { + input += "\n" + std::string(*line); needmore = processLine(input); if(!needmore) @@ -115,7 +88,6 @@ namespace repl // ok, we're done -- clear. input.clear(); } - #endif } } diff --git a/source/repl/parse.cpp b/source/repl/parse.cpp index 3b6477c6..3a4c786a 100644 --- a/source/repl/parse.cpp +++ b/source/repl/parse.cpp @@ -7,18 +7,44 @@ #include "frontend.h" #include "parser_internal.h" +#include "codegen.h" +#include "typecheck.h" + +#include "ir/module.h" +#include "ir/irbuilder.h" + #include "memorypool.h" +// defined in codegen/directives.cpp +fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, const Identifier& fname); + namespace repl { struct State { State() { - this->topLevelAst = util::pool(Location(), "__repl"); + auto modname = "__repl_mod__"; + + this->module = new fir::Module(modname); + + sst::StateTree* tree = new sst::StateTree(modname, modname, 0); + tree->treeDefn = util::pool(Location()); + tree->treeDefn->tree = tree; + + this->fs = new sst::TypecheckState(tree); + this->cs = new cgn::CodegenState(fir::IRBuilder(this->module)); + this->cs->module = this->module; + + // so we don't crash, give us a starting location. + this->cs->pushLoc(Location()); } - ast::TopLevelBlock* topLevelAst; + fir::Module* module; + sst::TypecheckState* fs; + cgn::CodegenState* cs; + + size_t counter = 0; }; static State* state = 0; @@ -39,21 +65,49 @@ namespace repl // parse, but first setup the environment. auto st = parser::State(lexResult.tokens); - auto stmt = parser::parseStmt(st, /* exprs: */ true); + auto _stmt = parser::parseStmt(st, /* exprs: */ true); - if(stmt.needsMoreTokens()) + if(_stmt.needsMoreTokens()) { return true; } - else if(stmt.isError()) + else if(_stmt.isError()) { - stmt.err()->post(); + _stmt.err()->post(); + return false; } - else + + // there's no need to fiddle with AST-level trees -- once we typecheck it, + // it will store the relevant state into the TypecheckState. { - state->topLevelAst->statements.push_back(stmt.val()); + auto stmt = _stmt.val(); + auto tcr = stmt->typecheck(state->fs); + + if(tcr.isError()) + { + tcr.error()->post(); + return false; + } + else if(!tcr.isParametric() && !tcr.isDummy()) + { + // copy some stuff over. + state->cs->typeDefnMap = state->fs->typeDefnMap; + + // ok, we have a thing. try to run it. + + auto value = magicallyRunExpressionAtCompileTime(state->cs, tcr.stmt(), nullptr, + Identifier(zpr::sprint("__anon_runner_%d", state->counter++), IdKind::Name)); + + if(value) + printf("%s\n", zpr::sprint("%s :: %s", value->str(), value->getType()).c_str()); + } } + + + + + return false; } } From ed089c15d6127a19f2c203fa4f769a402158607d Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 12 Nov 2019 00:07:42 +0800 Subject: [PATCH 065/129] delete useless things --- external/linenoise/LICENSE | 25 - external/linenoise/README.markdown | 229 ---- external/linenoise/encodings/utf8.c | 685 ------------ external/linenoise/encodings/utf8.h | 55 - external/linenoise/linenoise.c | 1328 ------------------------ external/linenoise/linenoise.h | 82 -- external/linenoise/makefile | 20 - makefile | 12 +- source/fir/ConstantValue.cpp | 2 +- source/include/ztmu.h | 2 +- source/repl/driver.cpp | 2 - source/repl/{parse.cpp => execute.cpp} | 24 +- 12 files changed, 24 insertions(+), 2442 deletions(-) delete mode 100644 external/linenoise/LICENSE delete mode 100644 external/linenoise/README.markdown delete mode 100755 external/linenoise/encodings/utf8.c delete mode 100755 external/linenoise/encodings/utf8.h delete mode 100644 external/linenoise/linenoise.c delete mode 100644 external/linenoise/linenoise.h delete mode 100644 external/linenoise/makefile rename source/repl/{parse.cpp => execute.cpp} (75%) diff --git a/external/linenoise/LICENSE b/external/linenoise/LICENSE deleted file mode 100644 index 18e81486..00000000 --- a/external/linenoise/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2010-2014, Salvatore Sanfilippo -Copyright (c) 2010-2013, Pieter Noordhuis - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/linenoise/README.markdown b/external/linenoise/README.markdown deleted file mode 100644 index 9f583c7a..00000000 --- a/external/linenoise/README.markdown +++ /dev/null @@ -1,229 +0,0 @@ -# Linenoise - -A minimal, zero-config, BSD licensed, readline replacement used in Redis, -MongoDB, and Android. - -* Single and multi line editing mode with the usual key bindings implemented. -* History handling. -* Completion. -* Hints (suggestions at the right of the prompt as you type). -* About 1,100 lines of BSD license source code. -* Only uses a subset of VT100 escapes (ANSI.SYS compatible). - -## Can a line editing library be 20k lines of code? - -Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing? - -So what usually happens is either: - - * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh). - * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance). - -The result is a pollution of binaries without line editing support. - -So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not. - -## Terminals, in 2010. - -Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no -VT220 specific sequences are used anymore. - -The library is currently about 1100 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software. - -## Tested with... - - * Linux text only console ($TERM = linux) - * Linux KDE terminal application ($TERM = xterm) - * Linux xterm ($TERM = xterm) - * Linux Buildroot ($TERM = vt100) - * Mac OS X iTerm ($TERM = xterm) - * Mac OS X default Terminal.app ($TERM = xterm) - * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) - * IBM AIX 6.1 - * FreeBSD xterm ($TERM = xterm) - * ANSI.SYS - * Emacs comint mode ($TERM = dumb) - -Please test it everywhere you can and report back! - -## Let's push this forward! - -Patches should be provided in the respect of Linenoise sensibility for small -easy to understand code. - -Send feedbacks to antirez at gmail - -# The API - -Linenoise is very easy to use, and reading the example shipped with the -library should get you up to speed ASAP. Here is a list of API calls -and how to use them. - - char *linenoise(const char *prompt); - -This is the main Linenoise call: it shows the user a prompt with line editing -and history capabilities. The prompt you specify is used as a prompt, that is, -it will be printed to the left of the cursor. The library returns a buffer -with the line composed by the user, or NULL on end of file or when there -is an out of memory condition. - -When a tty is detected (the user is actually typing into a terminal session) -the maximum editable line length is `LINENOISE_MAX_LINE`. When instead the -standard input is not a tty, which happens every time you redirect a file -to a program, or use it in an Unix pipeline, there are no limits to the -length of the line that can be returned. - -The returned line should be freed with the `free()` standard system call. -However sometimes it could happen that your program uses a different dynamic -allocation library, so you may also used `linenoiseFree` to make sure the -line is freed with the same allocator it was created. - -The canonical loop used by a program using Linenoise will be something like -this: - - while((line = linenoise("hello> ")) != NULL) { - printf("You wrote: %s\n", line); - linenoiseFree(line); /* Or just free(line) if you use libc malloc. */ - } - -## Single line VS multi line editing - -By default, Linenoise uses single line editing, that is, a single row on the -screen will be used, and as the user types more, the text will scroll towards -left to make room. This works if your program is one where the user is -unlikely to write a lot of text, otherwise multi line editing, where multiple -screens rows are used, can be a lot more comfortable. - -In order to enable multi line editing use the following API call: - - linenoiseSetMultiLine(1); - -You can disable it using `0` as argument. - -## History - -Linenoise supporst history, so that the user does not have to retype -again and again the same things, but can use the down and up arrows in order -to search and re-edit already inserted lines of text. - -The followings are the history API calls: - - int linenoiseHistoryAdd(const char *line); - int linenoiseHistorySetMaxLen(int len); - int linenoiseHistorySave(const char *filename); - int linenoiseHistoryLoad(const char *filename); - -Use `linenoiseHistoryAdd` every time you want to add a new element -to the top of the history (it will be the first the user will see when -using the up arrow). - -Note that for history to work, you have to set a length for the history -(which is zero by default, so history will be disabled if you don't set -a proper one). This is accomplished using the `linenoiseHistorySetMaxLen` -function. - -Linenoise has direct support for persisting the history into an history -file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do -just that. Both functions return -1 on error and 0 on success. - -## Completion - -Linenoise supports completion, which is the ability to complete the user -input when she or he presses the `` key. - -In order to use completion, you need to register a completion callback, which -is called every time the user presses ``. Your callback will return a -list of items that are completions for the current string. - -The following is an example of registering a completion callback: - - linenoiseSetCompletionCallback(completion); - -The completion must be a function returning `void` and getting as input -a `const char` pointer, which is the line the user has typed so far, and -a `linenoiseCompletions` object pointer, which is used as argument of -`linenoiseAddCompletion` in order to add completions inside the callback. -An example will make it more clear: - - void completion(const char *buf, linenoiseCompletions *lc) { - if (buf[0] == 'h') { - linenoiseAddCompletion(lc,"hello"); - linenoiseAddCompletion(lc,"hello there"); - } - } - -Basically in your completion callback, you inspect the input, and return -a list of items that are good completions by using `linenoiseAddCompletion`. - -If you want to test the completion feature, compile the example program -with `make`, run it, type `h` and press ``. - -## Hints - -Linenoise has a feature called *hints* which is very useful when you -use Linenoise in order to implement a REPL (Read Eval Print Loop) for -a program that accepts commands and arguments, but may also be useful in -other conditions. - -The feature shows, on the right of the cursor, as the user types, hints that -may be useful. The hints can be displayed using a different color compared -to the color the user is typing, and can also be bold. - -For example as the user starts to type `"git remote add"`, with hints it's -possible to show on the right of the prompt a string ` `. - -The feature works similarly to the history feature, using a callback. -To register the callback we use: - - linenoiseSetHintsCallback(hints); - -The callback itself is implemented like this: - - char *hints(const char *buf, int *color, int *bold) { - if (!strcasecmp(buf,"git remote add")) { - *color = 35; - *bold = 0; - return " "; - } - return NULL; - } - -The callback function returns the string that should be displayed or NULL -if no hint is available for the text the user currently typed. The returned -string will be trimmed as needed depending on the number of columns available -on the screen. - -It is possible to return a string allocated in dynamic way, by also registering -a function to deallocate the hint string once used: - - void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); - -The free hint callback will just receive the pointer and free the string -as needed (depending on how the hits callback allocated it). - -As you can see in the example above, a `color` (in xterm color terminal codes) -can be provided together with a `bold` attribute. If no color is set, the -current terminal foreground color is used. If no bold attribute is set, -non-bold text is printed. - -Color codes are: - - red = 31 - green = 32 - yellow = 33 - blue = 34 - magenta = 35 - cyan = 36 - white = 37; - -## Screen handling - -Sometimes you may want to clear the screen as a result of something the -user typed. You can do this by calling the following function: - - void linenoiseClearScreen(void); - -## Related projects - -* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language. -* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift. diff --git a/external/linenoise/encodings/utf8.c b/external/linenoise/encodings/utf8.c deleted file mode 100755 index 1b23f925..00000000 --- a/external/linenoise/encodings/utf8.c +++ /dev/null @@ -1,685 +0,0 @@ -/* encoding/utf8.c -- VERSION 1.0 - * - * Guerrilla line editing library against the idea that a line editing lib - * needs to be 20,000 lines of C code. - * - * You can find the latest source code at: - * - * http://github.com/antirez/linenoise - * - * Does a number of crazy assumptions that happen to be true in 99.9999% of - * the 2010 UNIX computers around. - * - * ------------------------------------------------------------------------ - * - * Copyright (c) 2010-2014, Salvatore Sanfilippo - * Copyright (c) 2010-2013, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -#define UNUSED(x) (void)(x) - -/* ============================ UTF8 utilities ============================== */ - -static unsigned long wideCharTable[][2] = { - { 0xA1, 0xA1 }, - { 0xA4, 0xA4 }, - { 0xA7, 0xA8 }, - { 0xAA, 0xAA }, - { 0xAD, 0xAE }, - { 0xB0, 0xB4 }, - { 0xB6, 0xBA }, - { 0xBC, 0xBF }, - { 0xC6, 0xC6 }, - { 0xD0, 0xD0 }, - { 0xD7, 0xD8 }, - { 0xDE, 0xE1 }, - { 0xE6, 0xE6 }, - { 0xE8, 0xEA }, - { 0xEC, 0xED }, - { 0xF0, 0xF0 }, - { 0xF2, 0xF3 }, - { 0xF7, 0xFA }, - { 0xFC, 0xFC }, - { 0xFE, 0xFE }, - { 0x101, 0x101 }, - { 0x111, 0x111 }, - { 0x113, 0x113 }, - { 0x11B, 0x11B }, - { 0x126, 0x127 }, - { 0x12B, 0x12B }, - { 0x131, 0x133 }, - { 0x138, 0x138 }, - { 0x13F, 0x142 }, - { 0x144, 0x144 }, - { 0x148, 0x14B }, - { 0x14D, 0x14D }, - { 0x152, 0x153 }, - { 0x166, 0x167 }, - { 0x16B, 0x16B }, - { 0x1CE, 0x1CE }, - { 0x1D0, 0x1D0 }, - { 0x1D2, 0x1D2 }, - { 0x1D4, 0x1D4 }, - { 0x1D6, 0x1D6 }, - { 0x1D8, 0x1D8 }, - { 0x1DA, 0x1DA }, - { 0x1DC, 0x1DC }, - { 0x251, 0x251 }, - { 0x261, 0x261 }, - { 0x2C4, 0x2C4 }, - { 0x2C7, 0x2C7 }, - { 0x2C9, 0x2CB }, - { 0x2CD, 0x2CD }, - { 0x2D0, 0x2D0 }, - { 0x2D8, 0x2DB }, - { 0x2DD, 0x2DD }, - { 0x2DF, 0x2DF }, - { 0x300, 0x36F }, - { 0x391, 0x3A1 }, - { 0x3A3, 0x3A9 }, - { 0x3B1, 0x3C1 }, - { 0x3C3, 0x3C9 }, - { 0x401, 0x401 }, - { 0x410, 0x44F }, - { 0x451, 0x451 }, - { 0x1100, 0x115F }, - { 0x2010, 0x2010 }, - { 0x2013, 0x2016 }, - { 0x2018, 0x2019 }, - { 0x201C, 0x201D }, - { 0x2020, 0x2022 }, - { 0x2024, 0x2027 }, - { 0x2030, 0x2030 }, - { 0x2032, 0x2033 }, - { 0x2035, 0x2035 }, - { 0x203B, 0x203B }, - { 0x203E, 0x203E }, - { 0x2074, 0x2074 }, - { 0x207F, 0x207F }, - { 0x2081, 0x2084 }, - { 0x20AC, 0x20AC }, - { 0x2103, 0x2103 }, - { 0x2105, 0x2105 }, - { 0x2109, 0x2109 }, - { 0x2113, 0x2113 }, - { 0x2116, 0x2116 }, - { 0x2121, 0x2122 }, - { 0x2126, 0x2126 }, - { 0x212B, 0x212B }, - { 0x2153, 0x2154 }, - { 0x215B, 0x215E }, - { 0x2160, 0x216B }, - { 0x2170, 0x2179 }, - { 0x2189, 0x2189 }, - { 0x2190, 0x2199 }, - { 0x21B8, 0x21B9 }, - { 0x21D2, 0x21D2 }, - { 0x21D4, 0x21D4 }, - { 0x21E7, 0x21E7 }, - { 0x2200, 0x2200 }, - { 0x2202, 0x2203 }, - { 0x2207, 0x2208 }, - { 0x220B, 0x220B }, - { 0x220F, 0x220F }, - { 0x2211, 0x2211 }, - { 0x2215, 0x2215 }, - { 0x221A, 0x221A }, - { 0x221D, 0x2220 }, - { 0x2223, 0x2223 }, - { 0x2225, 0x2225 }, - { 0x2227, 0x222C }, - { 0x222E, 0x222E }, - { 0x2234, 0x2237 }, - { 0x223C, 0x223D }, - { 0x2248, 0x2248 }, - { 0x224C, 0x224C }, - { 0x2252, 0x2252 }, - { 0x2260, 0x2261 }, - { 0x2264, 0x2267 }, - { 0x226A, 0x226B }, - { 0x226E, 0x226F }, - { 0x2282, 0x2283 }, - { 0x2286, 0x2287 }, - { 0x2295, 0x2295 }, - { 0x2299, 0x2299 }, - { 0x22A5, 0x22A5 }, - { 0x22BF, 0x22BF }, - { 0x2312, 0x2312 }, - { 0x231A, 0x231B }, - { 0x2329, 0x232A }, - { 0x23E9, 0x23EC }, - { 0x23F0, 0x23F0 }, - { 0x23F3, 0x23F3 }, - { 0x2460, 0x24E9 }, - { 0x24EB, 0x254B }, - { 0x2550, 0x2573 }, - { 0x2580, 0x258F }, - { 0x2592, 0x2595 }, - { 0x25A0, 0x25A1 }, - { 0x25A3, 0x25A9 }, - { 0x25B2, 0x25B3 }, - { 0x25B6, 0x25B7 }, - { 0x25BC, 0x25BD }, - { 0x25C0, 0x25C1 }, - { 0x25C6, 0x25C8 }, - { 0x25CB, 0x25CB }, - { 0x25CE, 0x25D1 }, - { 0x25E2, 0x25E5 }, - { 0x25EF, 0x25EF }, - { 0x25FD, 0x25FE }, - { 0x2605, 0x2606 }, - { 0x2609, 0x2609 }, - { 0x260E, 0x260F }, - { 0x2614, 0x2615 }, - { 0x261C, 0x261C }, - { 0x261E, 0x261E }, - { 0x2640, 0x2640 }, - { 0x2642, 0x2642 }, - { 0x2648, 0x2653 }, - { 0x2660, 0x2661 }, - { 0x2663, 0x2665 }, - { 0x2667, 0x266A }, - { 0x266C, 0x266D }, - { 0x266F, 0x266F }, - { 0x267F, 0x267F }, - { 0x2693, 0x2693 }, - { 0x269E, 0x269F }, - { 0x26A1, 0x26A1 }, - { 0x26AA, 0x26AB }, - { 0x26BD, 0x26BF }, - { 0x26C4, 0x26E1 }, - { 0x26E3, 0x26E3 }, - { 0x26E8, 0x26FF }, - { 0x2705, 0x2705 }, - { 0x270A, 0x270B }, - { 0x2728, 0x2728 }, - { 0x273D, 0x273D }, - { 0x274C, 0x274C }, - { 0x274E, 0x274E }, - { 0x2753, 0x2755 }, - { 0x2757, 0x2757 }, - { 0x2776, 0x277F }, - { 0x2795, 0x2797 }, - { 0x27B0, 0x27B0 }, - { 0x27BF, 0x27BF }, - { 0x2B1B, 0x2B1C }, - { 0x2B50, 0x2B50 }, - { 0x2B55, 0x2B59 }, - { 0x2E80, 0x2E99 }, - { 0x2E9B, 0x2EF3 }, - { 0x2F00, 0x2FD5 }, - { 0x2FF0, 0x2FFB }, - { 0x3000, 0x303E }, - { 0x3041, 0x3096 }, - { 0x3099, 0x30FF }, - { 0x3105, 0x312F }, - { 0x3131, 0x318E }, - { 0x3190, 0x31BA }, - { 0x31C0, 0x31E3 }, - { 0x31F0, 0x321E }, - { 0x3220, 0x4DBF }, - { 0x4E00, 0xA48C }, - { 0xA490, 0xA4C6 }, - { 0xA960, 0xA97C }, - { 0xAC00, 0xD7A3 }, - { 0xE000, 0xFAFF }, - { 0xFE00, 0xFE19 }, - { 0xFE30, 0xFE52 }, - { 0xFE54, 0xFE66 }, - { 0xFE68, 0xFE6B }, - { 0xFF01, 0xFF60 }, - { 0xFFE0, 0xFFE6 }, - { 0xFFFD, 0xFFFD }, - { 0x16FE0, 0x16FE3 }, - { 0x17000, 0x187F7 }, - { 0x18800, 0x18AF2 }, - { 0x1B000, 0x1B11E }, - { 0x1B150, 0x1B152 }, - { 0x1B164, 0x1B167 }, - { 0x1B170, 0x1B2FB }, - { 0x1F004, 0x1F004 }, - { 0x1F0CF, 0x1F0CF }, - { 0x1F100, 0x1F10A }, - { 0x1F110, 0x1F12D }, - { 0x1F130, 0x1F169 }, - { 0x1F170, 0x1F1AC }, - { 0x1F200, 0x1F202 }, - { 0x1F210, 0x1F23B }, - { 0x1F240, 0x1F248 }, - { 0x1F250, 0x1F251 }, - { 0x1F260, 0x1F265 }, - { 0x1F300, 0x1F320 }, - { 0x1F32D, 0x1F335 }, - { 0x1F337, 0x1F37C }, - { 0x1F37E, 0x1F393 }, - { 0x1F3A0, 0x1F3CA }, - { 0x1F3CF, 0x1F3D3 }, - { 0x1F3E0, 0x1F3F0 }, - { 0x1F3F4, 0x1F3F4 }, - { 0x1F3F8, 0x1F43E }, - { 0x1F440, 0x1F440 }, - { 0x1F442, 0x1F4FC }, - { 0x1F4FF, 0x1F53D }, - { 0x1F54B, 0x1F54E }, - { 0x1F550, 0x1F567 }, - { 0x1F57A, 0x1F57A }, - { 0x1F595, 0x1F596 }, - { 0x1F5A4, 0x1F5A4 }, - { 0x1F5FB, 0x1F64F }, - { 0x1F680, 0x1F6C5 }, - { 0x1F6CC, 0x1F6CC }, - { 0x1F6D0, 0x1F6D2 }, - { 0x1F6D5, 0x1F6D5 }, - { 0x1F6EB, 0x1F6EC }, - { 0x1F6F4, 0x1F6FA }, - { 0x1F7E0, 0x1F7EB }, - { 0x1F90D, 0x1F971 }, - { 0x1F973, 0x1F976 }, - { 0x1F97A, 0x1F9A2 }, - { 0x1F9A5, 0x1F9AA }, - { 0x1F9AE, 0x1F9CA }, - { 0x1F9CD, 0x1F9FF }, - { 0x1FA70, 0x1FA73 }, - { 0x1FA78, 0x1FA7A }, - { 0x1FA80, 0x1FA82 }, - { 0x1FA90, 0x1FA95 }, - { 0x20000, 0x2FFFD }, - { 0x30000, 0x3FFFD }, - { 0xE0100, 0xE01EF }, - { 0xF0000, 0xFFFFD }, - { 0x100000, 0x10FFFD }, -}; - -static size_t wideCharTableSize = sizeof(wideCharTable) / sizeof(wideCharTable[0]); - -static unsigned long combiningCharTable[] = { - 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, - 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, - 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, - 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, - 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, - 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, - 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, - 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, - 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0591, 0x0592, 0x0593, - 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B, - 0x059C, 0x059D, 0x059E, 0x059F, 0x05A0, 0x05A1, 0x05A2, 0x05A3, - 0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB, - 0x05AC, 0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3, - 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB, - 0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7, - 0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, - 0x0618, 0x0619, 0x061A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, - 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657, - 0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F, - 0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC, - 0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7, 0x06E8, - 0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0711, 0x0730, 0x0731, 0x0732, - 0x0733, 0x0734, 0x0735, 0x0736, 0x0737, 0x0738, 0x0739, 0x073A, - 0x073B, 0x073C, 0x073D, 0x073E, 0x073F, 0x0740, 0x0741, 0x0742, - 0x0743, 0x0744, 0x0745, 0x0746, 0x0747, 0x0748, 0x0749, 0x074A, - 0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD, - 0x07AE, 0x07AF, 0x07B0, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF, - 0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07FD, 0x0816, 0x0817, 0x0818, - 0x0819, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821, - 0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082A, 0x082B, - 0x082C, 0x082D, 0x0859, 0x085A, 0x085B, 0x08D3, 0x08D4, 0x08D5, - 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB, 0x08DC, 0x08DD, - 0x08DE, 0x08DF, 0x08E0, 0x08E1, 0x08E3, 0x08E4, 0x08E5, 0x08E6, - 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC, 0x08ED, 0x08EE, - 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4, 0x08F5, 0x08F6, - 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC, 0x08FD, 0x08FE, - 0x08FF, 0x0900, 0x0901, 0x0902, 0x093A, 0x093C, 0x0941, 0x0942, - 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, 0x0948, 0x094D, 0x0951, - 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, 0x0962, 0x0963, - 0x0981, 0x09BC, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09CD, 0x09E2, - 0x09E3, 0x09FE, 0x0A01, 0x0A02, 0x0A3C, 0x0A41, 0x0A42, 0x0A47, - 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A51, 0x0A70, 0x0A71, 0x0A75, - 0x0A81, 0x0A82, 0x0ABC, 0x0AC1, 0x0AC2, 0x0AC3, 0x0AC4, 0x0AC5, - 0x0AC7, 0x0AC8, 0x0ACD, 0x0AE2, 0x0AE3, 0x0AFA, 0x0AFB, 0x0AFC, - 0x0AFD, 0x0AFE, 0x0AFF, 0x0B01, 0x0B3C, 0x0B3F, 0x0B41, 0x0B42, - 0x0B43, 0x0B44, 0x0B4D, 0x0B56, 0x0B62, 0x0B63, 0x0B82, 0x0BC0, - 0x0BCD, 0x0C00, 0x0C04, 0x0C3E, 0x0C3F, 0x0C40, 0x0C46, 0x0C47, - 0x0C48, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D, 0x0C55, 0x0C56, 0x0C62, - 0x0C63, 0x0C81, 0x0CBC, 0x0CBF, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, - 0x0CE3, 0x0D00, 0x0D01, 0x0D3B, 0x0D3C, 0x0D41, 0x0D42, 0x0D43, - 0x0D44, 0x0D4D, 0x0D62, 0x0D63, 0x0DCA, 0x0DD2, 0x0DD3, 0x0DD4, - 0x0DD6, 0x0E31, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, - 0x0E3A, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, - 0x0E4E, 0x0EB1, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, 0x0EB8, 0x0EB9, - 0x0EBA, 0x0EBB, 0x0EBC, 0x0EC8, 0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, - 0x0ECD, 0x0F18, 0x0F19, 0x0F35, 0x0F37, 0x0F39, 0x0F71, 0x0F72, - 0x0F73, 0x0F74, 0x0F75, 0x0F76, 0x0F77, 0x0F78, 0x0F79, 0x0F7A, - 0x0F7B, 0x0F7C, 0x0F7D, 0x0F7E, 0x0F80, 0x0F81, 0x0F82, 0x0F83, - 0x0F84, 0x0F86, 0x0F87, 0x0F8D, 0x0F8E, 0x0F8F, 0x0F90, 0x0F91, - 0x0F92, 0x0F93, 0x0F94, 0x0F95, 0x0F96, 0x0F97, 0x0F99, 0x0F9A, - 0x0F9B, 0x0F9C, 0x0F9D, 0x0F9E, 0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, - 0x0FA3, 0x0FA4, 0x0FA5, 0x0FA6, 0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, - 0x0FAB, 0x0FAC, 0x0FAD, 0x0FAE, 0x0FAF, 0x0FB0, 0x0FB1, 0x0FB2, - 0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6, 0x0FB7, 0x0FB8, 0x0FB9, 0x0FBA, - 0x0FBB, 0x0FBC, 0x0FC6, 0x102D, 0x102E, 0x102F, 0x1030, 0x1032, - 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1039, 0x103A, 0x103D, - 0x103E, 0x1058, 0x1059, 0x105E, 0x105F, 0x1060, 0x1071, 0x1072, - 0x1073, 0x1074, 0x1082, 0x1085, 0x1086, 0x108D, 0x109D, 0x135D, - 0x135E, 0x135F, 0x1712, 0x1713, 0x1714, 0x1732, 0x1733, 0x1734, - 0x1752, 0x1753, 0x1772, 0x1773, 0x17B4, 0x17B5, 0x17B7, 0x17B8, - 0x17B9, 0x17BA, 0x17BB, 0x17BC, 0x17BD, 0x17C6, 0x17C9, 0x17CA, - 0x17CB, 0x17CC, 0x17CD, 0x17CE, 0x17CF, 0x17D0, 0x17D1, 0x17D2, - 0x17D3, 0x17DD, 0x180B, 0x180C, 0x180D, 0x1885, 0x1886, 0x18A9, - 0x1920, 0x1921, 0x1922, 0x1927, 0x1928, 0x1932, 0x1939, 0x193A, - 0x193B, 0x1A17, 0x1A18, 0x1A1B, 0x1A56, 0x1A58, 0x1A59, 0x1A5A, - 0x1A5B, 0x1A5C, 0x1A5D, 0x1A5E, 0x1A60, 0x1A62, 0x1A65, 0x1A66, - 0x1A67, 0x1A68, 0x1A69, 0x1A6A, 0x1A6B, 0x1A6C, 0x1A73, 0x1A74, - 0x1A75, 0x1A76, 0x1A77, 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, - 0x1A7F, 0x1AB0, 0x1AB1, 0x1AB2, 0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, - 0x1AB7, 0x1AB8, 0x1AB9, 0x1ABA, 0x1ABB, 0x1ABC, 0x1ABD, 0x1B00, - 0x1B01, 0x1B02, 0x1B03, 0x1B34, 0x1B36, 0x1B37, 0x1B38, 0x1B39, - 0x1B3A, 0x1B3C, 0x1B42, 0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, - 0x1B70, 0x1B71, 0x1B72, 0x1B73, 0x1B80, 0x1B81, 0x1BA2, 0x1BA3, - 0x1BA4, 0x1BA5, 0x1BA8, 0x1BA9, 0x1BAB, 0x1BAC, 0x1BAD, 0x1BE6, - 0x1BE8, 0x1BE9, 0x1BED, 0x1BEF, 0x1BF0, 0x1BF1, 0x1C2C, 0x1C2D, - 0x1C2E, 0x1C2F, 0x1C30, 0x1C31, 0x1C32, 0x1C33, 0x1C36, 0x1C37, - 0x1CD0, 0x1CD1, 0x1CD2, 0x1CD4, 0x1CD5, 0x1CD6, 0x1CD7, 0x1CD8, - 0x1CD9, 0x1CDA, 0x1CDB, 0x1CDC, 0x1CDD, 0x1CDE, 0x1CDF, 0x1CE0, - 0x1CE2, 0x1CE3, 0x1CE4, 0x1CE5, 0x1CE6, 0x1CE7, 0x1CE8, 0x1CED, - 0x1CF4, 0x1CF8, 0x1CF9, 0x1DC0, 0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, - 0x1DC5, 0x1DC6, 0x1DC7, 0x1DC8, 0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, - 0x1DCD, 0x1DCE, 0x1DCF, 0x1DD0, 0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, - 0x1DD5, 0x1DD6, 0x1DD7, 0x1DD8, 0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, - 0x1DDD, 0x1DDE, 0x1DDF, 0x1DE0, 0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, - 0x1DE5, 0x1DE6, 0x1DE7, 0x1DE8, 0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, - 0x1DED, 0x1DEE, 0x1DEF, 0x1DF0, 0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, - 0x1DF5, 0x1DF6, 0x1DF7, 0x1DF8, 0x1DF9, 0x1DFB, 0x1DFC, 0x1DFD, - 0x1DFE, 0x1DFF, 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, - 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20E1, - 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, - 0x20ED, 0x20EE, 0x20EF, 0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2D7F, - 0x2DE0, 0x2DE1, 0x2DE2, 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, - 0x2DE8, 0x2DE9, 0x2DEA, 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, 0x2DEF, - 0x2DF0, 0x2DF1, 0x2DF2, 0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, - 0x2DF8, 0x2DF9, 0x2DFA, 0x2DFB, 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, - 0x302A, 0x302B, 0x302C, 0x302D, 0x3099, 0x309A, 0xA66F, 0xA674, - 0xA675, 0xA676, 0xA677, 0xA678, 0xA679, 0xA67A, 0xA67B, 0xA67C, - 0xA67D, 0xA69E, 0xA69F, 0xA6F0, 0xA6F1, 0xA802, 0xA806, 0xA80B, - 0xA825, 0xA826, 0xA8C4, 0xA8C5, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3, - 0xA8E4, 0xA8E5, 0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB, - 0xA8EC, 0xA8ED, 0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xA8FF, 0xA926, - 0xA927, 0xA928, 0xA929, 0xA92A, 0xA92B, 0xA92C, 0xA92D, 0xA947, - 0xA948, 0xA949, 0xA94A, 0xA94B, 0xA94C, 0xA94D, 0xA94E, 0xA94F, - 0xA950, 0xA951, 0xA980, 0xA981, 0xA982, 0xA9B3, 0xA9B6, 0xA9B7, - 0xA9B8, 0xA9B9, 0xA9BC, 0xA9BD, 0xA9E5, 0xAA29, 0xAA2A, 0xAA2B, - 0xAA2C, 0xAA2D, 0xAA2E, 0xAA31, 0xAA32, 0xAA35, 0xAA36, 0xAA43, - 0xAA4C, 0xAA7C, 0xAAB0, 0xAAB2, 0xAAB3, 0xAAB4, 0xAAB7, 0xAAB8, - 0xAABE, 0xAABF, 0xAAC1, 0xAAEC, 0xAAED, 0xAAF6, 0xABE5, 0xABE8, - 0xABED, 0xFB1E, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, - 0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, - 0xFE0E, 0xFE0F, 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, - 0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, - 0xFE2E, 0xFE2F, 0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379, - 0x1037A, 0x10A01, 0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0D, - 0x10A0E, 0x10A0F, 0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6, - 0x10D24, 0x10D25, 0x10D26, 0x10D27, 0x10F46, 0x10F47, 0x10F48, 0x10F49, - 0x10F4A, 0x10F4B, 0x10F4C, 0x10F4D, 0x10F4E, 0x10F4F, 0x10F50, 0x11001, - 0x11038, 0x11039, 0x1103A, 0x1103B, 0x1103C, 0x1103D, 0x1103E, 0x1103F, - 0x11040, 0x11041, 0x11042, 0x11043, 0x11044, 0x11045, 0x11046, 0x1107F, - 0x11080, 0x11081, 0x110B3, 0x110B4, 0x110B5, 0x110B6, 0x110B9, 0x110BA, - 0x11100, 0x11101, 0x11102, 0x11127, 0x11128, 0x11129, 0x1112A, 0x1112B, - 0x1112D, 0x1112E, 0x1112F, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134, - 0x11173, 0x11180, 0x11181, 0x111B6, 0x111B7, 0x111B8, 0x111B9, 0x111BA, - 0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111C9, 0x111CA, 0x111CB, 0x111CC, - 0x1122F, 0x11230, 0x11231, 0x11234, 0x11236, 0x11237, 0x1123E, 0x112DF, - 0x112E3, 0x112E4, 0x112E5, 0x112E6, 0x112E7, 0x112E8, 0x112E9, 0x112EA, - 0x11300, 0x11301, 0x1133B, 0x1133C, 0x11340, 0x11366, 0x11367, 0x11368, - 0x11369, 0x1136A, 0x1136B, 0x1136C, 0x11370, 0x11371, 0x11372, 0x11373, - 0x11374, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D, 0x1143E, - 0x1143F, 0x11442, 0x11443, 0x11444, 0x11446, 0x1145E, 0x114B3, 0x114B4, - 0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114BA, 0x114BF, 0x114C0, 0x114C2, - 0x114C3, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115BC, 0x115BD, 0x115BF, - 0x115C0, 0x115DC, 0x115DD, 0x11633, 0x11634, 0x11635, 0x11636, 0x11637, - 0x11638, 0x11639, 0x1163A, 0x1163D, 0x1163F, 0x11640, 0x116AB, 0x116AD, - 0x116B0, 0x116B1, 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B7, 0x1171D, - 0x1171E, 0x1171F, 0x11722, 0x11723, 0x11724, 0x11725, 0x11727, 0x11728, - 0x11729, 0x1172A, 0x1172B, 0x1182F, 0x11830, 0x11831, 0x11832, 0x11833, - 0x11834, 0x11835, 0x11836, 0x11837, 0x11839, 0x1183A, 0x119D4, 0x119D5, - 0x119D6, 0x119D7, 0x119DA, 0x119DB, 0x119E0, 0x11A01, 0x11A02, 0x11A03, - 0x11A04, 0x11A05, 0x11A06, 0x11A07, 0x11A08, 0x11A09, 0x11A0A, 0x11A33, - 0x11A34, 0x11A35, 0x11A36, 0x11A37, 0x11A38, 0x11A3B, 0x11A3C, 0x11A3D, - 0x11A3E, 0x11A47, 0x11A51, 0x11A52, 0x11A53, 0x11A54, 0x11A55, 0x11A56, - 0x11A59, 0x11A5A, 0x11A5B, 0x11A8A, 0x11A8B, 0x11A8C, 0x11A8D, 0x11A8E, - 0x11A8F, 0x11A90, 0x11A91, 0x11A92, 0x11A93, 0x11A94, 0x11A95, 0x11A96, - 0x11A98, 0x11A99, 0x11C30, 0x11C31, 0x11C32, 0x11C33, 0x11C34, 0x11C35, - 0x11C36, 0x11C38, 0x11C39, 0x11C3A, 0x11C3B, 0x11C3C, 0x11C3D, 0x11C3F, - 0x11C92, 0x11C93, 0x11C94, 0x11C95, 0x11C96, 0x11C97, 0x11C98, 0x11C99, - 0x11C9A, 0x11C9B, 0x11C9C, 0x11C9D, 0x11C9E, 0x11C9F, 0x11CA0, 0x11CA1, - 0x11CA2, 0x11CA3, 0x11CA4, 0x11CA5, 0x11CA6, 0x11CA7, 0x11CAA, 0x11CAB, - 0x11CAC, 0x11CAD, 0x11CAE, 0x11CAF, 0x11CB0, 0x11CB2, 0x11CB3, 0x11CB5, - 0x11CB6, 0x11D31, 0x11D32, 0x11D33, 0x11D34, 0x11D35, 0x11D36, 0x11D3A, - 0x11D3C, 0x11D3D, 0x11D3F, 0x11D40, 0x11D41, 0x11D42, 0x11D43, 0x11D44, - 0x11D45, 0x11D47, 0x11D90, 0x11D91, 0x11D95, 0x11D97, 0x11EF3, 0x11EF4, - 0x16AF0, 0x16AF1, 0x16AF2, 0x16AF3, 0x16AF4, 0x16B30, 0x16B31, 0x16B32, - 0x16B33, 0x16B34, 0x16B35, 0x16B36, 0x16F4F, 0x16F8F, 0x16F90, 0x16F91, - 0x16F92, 0x1BC9D, 0x1BC9E, 0x1D167, 0x1D168, 0x1D169, 0x1D17B, 0x1D17C, - 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181, 0x1D182, 0x1D185, 0x1D186, - 0x1D187, 0x1D188, 0x1D189, 0x1D18A, 0x1D18B, 0x1D1AA, 0x1D1AB, 0x1D1AC, - 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00, 0x1DA01, 0x1DA02, 0x1DA03, - 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08, 0x1DA09, 0x1DA0A, 0x1DA0B, - 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10, 0x1DA11, 0x1DA12, 0x1DA13, - 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18, 0x1DA19, 0x1DA1A, 0x1DA1B, - 0x1DA1C, 0x1DA1D, 0x1DA1E, 0x1DA1F, 0x1DA20, 0x1DA21, 0x1DA22, 0x1DA23, - 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28, 0x1DA29, 0x1DA2A, 0x1DA2B, - 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30, 0x1DA31, 0x1DA32, 0x1DA33, - 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, 0x1DA3C, 0x1DA3D, 0x1DA3E, 0x1DA3F, - 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44, 0x1DA45, 0x1DA46, 0x1DA47, - 0x1DA48, 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C, 0x1DA4D, 0x1DA4E, 0x1DA4F, - 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54, 0x1DA55, 0x1DA56, 0x1DA57, - 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C, 0x1DA5D, 0x1DA5E, 0x1DA5F, - 0x1DA60, 0x1DA61, 0x1DA62, 0x1DA63, 0x1DA64, 0x1DA65, 0x1DA66, 0x1DA67, - 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C, 0x1DA75, 0x1DA84, 0x1DA9B, - 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1, 0x1DAA2, 0x1DAA3, 0x1DAA4, - 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9, 0x1DAAA, 0x1DAAB, 0x1DAAC, - 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001, 0x1E002, 0x1E003, 0x1E004, - 0x1E005, 0x1E006, 0x1E008, 0x1E009, 0x1E00A, 0x1E00B, 0x1E00C, 0x1E00D, - 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012, 0x1E013, 0x1E014, 0x1E015, - 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C, 0x1E01D, 0x1E01E, 0x1E01F, - 0x1E020, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E027, 0x1E028, 0x1E029, - 0x1E02A, 0x1E130, 0x1E131, 0x1E132, 0x1E133, 0x1E134, 0x1E135, 0x1E136, - 0x1E2EC, 0x1E2ED, 0x1E2EE, 0x1E2EF, 0x1E8D0, 0x1E8D1, 0x1E8D2, 0x1E8D3, - 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944, 0x1E945, 0x1E946, 0x1E947, 0x1E948, - 0x1E949, 0x1E94A, 0xE0100, 0xE0101, 0xE0102, 0xE0103, 0xE0104, 0xE0105, - 0xE0106, 0xE0107, 0xE0108, 0xE0109, 0xE010A, 0xE010B, 0xE010C, 0xE010D, - 0xE010E, 0xE010F, 0xE0110, 0xE0111, 0xE0112, 0xE0113, 0xE0114, 0xE0115, - 0xE0116, 0xE0117, 0xE0118, 0xE0119, 0xE011A, 0xE011B, 0xE011C, 0xE011D, - 0xE011E, 0xE011F, 0xE0120, 0xE0121, 0xE0122, 0xE0123, 0xE0124, 0xE0125, - 0xE0126, 0xE0127, 0xE0128, 0xE0129, 0xE012A, 0xE012B, 0xE012C, 0xE012D, - 0xE012E, 0xE012F, 0xE0130, 0xE0131, 0xE0132, 0xE0133, 0xE0134, 0xE0135, - 0xE0136, 0xE0137, 0xE0138, 0xE0139, 0xE013A, 0xE013B, 0xE013C, 0xE013D, - 0xE013E, 0xE013F, 0xE0140, 0xE0141, 0xE0142, 0xE0143, 0xE0144, 0xE0145, - 0xE0146, 0xE0147, 0xE0148, 0xE0149, 0xE014A, 0xE014B, 0xE014C, 0xE014D, - 0xE014E, 0xE014F, 0xE0150, 0xE0151, 0xE0152, 0xE0153, 0xE0154, 0xE0155, - 0xE0156, 0xE0157, 0xE0158, 0xE0159, 0xE015A, 0xE015B, 0xE015C, 0xE015D, - 0xE015E, 0xE015F, 0xE0160, 0xE0161, 0xE0162, 0xE0163, 0xE0164, 0xE0165, - 0xE0166, 0xE0167, 0xE0168, 0xE0169, 0xE016A, 0xE016B, 0xE016C, 0xE016D, - 0xE016E, 0xE016F, 0xE0170, 0xE0171, 0xE0172, 0xE0173, 0xE0174, 0xE0175, - 0xE0176, 0xE0177, 0xE0178, 0xE0179, 0xE017A, 0xE017B, 0xE017C, 0xE017D, - 0xE017E, 0xE017F, 0xE0180, 0xE0181, 0xE0182, 0xE0183, 0xE0184, 0xE0185, - 0xE0186, 0xE0187, 0xE0188, 0xE0189, 0xE018A, 0xE018B, 0xE018C, 0xE018D, - 0xE018E, 0xE018F, 0xE0190, 0xE0191, 0xE0192, 0xE0193, 0xE0194, 0xE0195, - 0xE0196, 0xE0197, 0xE0198, 0xE0199, 0xE019A, 0xE019B, 0xE019C, 0xE019D, - 0xE019E, 0xE019F, 0xE01A0, 0xE01A1, 0xE01A2, 0xE01A3, 0xE01A4, 0xE01A5, - 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9, 0xE01AA, 0xE01AB, 0xE01AC, 0xE01AD, - 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1, 0xE01B2, 0xE01B3, 0xE01B4, 0xE01B5, - 0xE01B6, 0xE01B7, 0xE01B8, 0xE01B9, 0xE01BA, 0xE01BB, 0xE01BC, 0xE01BD, - 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1, 0xE01C2, 0xE01C3, 0xE01C4, 0xE01C5, - 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9, 0xE01CA, 0xE01CB, 0xE01CC, 0xE01CD, - 0xE01CE, 0xE01CF, 0xE01D0, 0xE01D1, 0xE01D2, 0xE01D3, 0xE01D4, 0xE01D5, - 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9, 0xE01DA, 0xE01DB, 0xE01DC, 0xE01DD, - 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1, 0xE01E2, 0xE01E3, 0xE01E4, 0xE01E5, - 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9, 0xE01EA, 0xE01EB, 0xE01EC, 0xE01ED, - 0xE01EE, 0xE01EF, -}; - -static unsigned long combiningCharTableSize = sizeof(combiningCharTable) / sizeof(combiningCharTable[0]); - -/* Check if the code is a wide character - */ -static int isWideChar(unsigned long cp) { - size_t i; - for (i = 0; i < wideCharTableSize; i++) - if (wideCharTable[i][0] <= cp && cp <= wideCharTable[i][1]) return 1; - return 0; -} - -/* Check if the code is a combining character - */ -static int isCombiningChar(unsigned long cp) { - size_t i; - for (i = 0; i < combiningCharTableSize; i++) - if (combiningCharTable[i] == cp) return 1; - return 0; -} - -/* Get length of previous UTF8 character - */ -static size_t prevUtf8CharLen(const char* buf, int pos) { - int end = pos--; - while (pos >= 0 && ((unsigned char)buf[pos] & 0xC0) == 0x80) - pos--; - return end - pos; -} - -/* Convert UTF8 to Unicode code point - */ -static size_t utf8BytesToCodePoint(const char* buf, size_t len, int* cp) { - if (len) { - unsigned char byte = buf[0]; - if ((byte & 0x80) == 0) { - *cp = byte; - return 1; - } else if ((byte & 0xE0) == 0xC0) { - if (len >= 2) { - *cp = (((unsigned long)(buf[0] & 0x1F)) << 6) | - ((unsigned long)(buf[1] & 0x3F)); - return 2; - } - } else if ((byte & 0xF0) == 0xE0) { - if (len >= 3) { - *cp = (((unsigned long)(buf[0] & 0x0F)) << 12) | - (((unsigned long)(buf[1] & 0x3F)) << 6) | - ((unsigned long)(buf[2] & 0x3F)); - return 3; - } - } else if ((byte & 0xF8) == 0xF0) { - if (len >= 4) { - *cp = (((unsigned long)(buf[0] & 0x07)) << 18) | - (((unsigned long)(buf[1] & 0x3F)) << 12) | - (((unsigned long)(buf[2] & 0x3F)) << 6) | - ((unsigned long)(buf[3] & 0x3F)); - return 4; - } - } - } - return 0; -} - -/* Get length of next grapheme - */ -size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) { - size_t beg = pos; - int cp; - size_t len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp); - if (isCombiningChar(cp)) { - /* NOTREACHED */ - return 0; - } - if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1; - pos += len; - while (pos < buf_len) { - int cp; - len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp); - if (!isCombiningChar(cp)) return pos - beg; - pos += len; - } - return pos - beg; -} - -/* Get length of previous grapheme - */ -size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) { - UNUSED(buf_len); - size_t end = pos; - while (pos > 0) { - size_t len = prevUtf8CharLen(buf, pos); - pos -= len; - int cp; - utf8BytesToCodePoint(buf + pos, len, &cp); - if (!isCombiningChar(cp)) { - if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1; - return end - pos; - } - } - /* NOTREACHED */ - return 0; -} - -/* Read a Unicode from file. - */ -size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp) { - if (buf_len < 1) return -1; - size_t nread = read(fd,&buf[0],1); - if (nread <= 0) return nread; - - unsigned char byte = buf[0]; - if ((byte & 0x80) == 0) { - ; - } else if ((byte & 0xE0) == 0xC0) { - if (buf_len < 2) return -1; - nread = read(fd,&buf[1],1); - if (nread <= 0) return nread; - } else if ((byte & 0xF0) == 0xE0) { - if (buf_len < 3) return -1; - nread = read(fd,&buf[1],2); - if (nread <= 0) return nread; - } else if ((byte & 0xF8) == 0xF0) { - if (buf_len < 3) return -1; - nread = read(fd,&buf[1],3); - if (nread <= 0) return nread; - } else { - return -1; - } - - return utf8BytesToCodePoint(buf, buf_len, cp); -} diff --git a/external/linenoise/encodings/utf8.h b/external/linenoise/encodings/utf8.h deleted file mode 100755 index d401bc86..00000000 --- a/external/linenoise/encodings/utf8.h +++ /dev/null @@ -1,55 +0,0 @@ -/* encodings/utf8.h -- VERSION 1.0 - * - * Guerrilla line editing library against the idea that a line editing lib - * needs to be 20,000 lines of C code. - * - * See linenoise.c for more information. - * - * ------------------------------------------------------------------------ - * - * Copyright (c) 2010-2014, Salvatore Sanfilippo - * Copyright (c) 2010-2013, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __LINENOISE_ENCODINGS_UTF8_H -#define __LINENOISE_ENCODINGS_UTF8_H - -#ifdef __cplusplus -extern "C" { -#endif - -size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len); -size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len); -size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp); - -#ifdef __cplusplus -} -#endif - -#endif /* __LINENOISE_ENCODINGS_UTF8_H */ - diff --git a/external/linenoise/linenoise.c b/external/linenoise/linenoise.c deleted file mode 100644 index 1550f197..00000000 --- a/external/linenoise/linenoise.c +++ /dev/null @@ -1,1328 +0,0 @@ -/* linenoise.c -- guerrilla line editing library against the idea that a - * line editing lib needs to be 20,000 lines of C code. - * - * You can find the latest source code at: - * - * http://github.com/antirez/linenoise - * - * Does a number of crazy assumptions that happen to be true in 99.9999% of - * the 2010 UNIX computers around. - * - * ------------------------------------------------------------------------ - * - * Copyright (c) 2010-2016, Salvatore Sanfilippo - * Copyright (c) 2010-2013, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ------------------------------------------------------------------------ - * - * References: - * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html - * - * Todo list: - * - Filter bogus Ctrl+ combinations. - * - Win32 support - * - * Bloat: - * - History search like Ctrl+r in readline? - * - * List of escape sequences used by this program, we do everything just - * with three sequences. In order to be so cheap we may have some - * flickering effect with some slow terminal, but the lesser sequences - * the more compatible. - * - * EL (Erase Line) - * Sequence: ESC [ n K - * Effect: if n is 0 or missing, clear from cursor to end of line - * Effect: if n is 1, clear from beginning of line to cursor - * Effect: if n is 2, clear entire line - * - * CUF (CUrsor Forward) - * Sequence: ESC [ n C - * Effect: moves cursor forward n chars - * - * CUB (CUrsor Backward) - * Sequence: ESC [ n D - * Effect: moves cursor backward n chars - * - * The following is used to get the terminal width if getting - * the width with the TIOCGWINSZ ioctl fails - * - * DSR (Device Status Report) - * Sequence: ESC [ 6 n - * Effect: reports the current cusor position as ESC [ n ; m R - * where n is the row and m is the column - * - * When multi line mode is enabled, we also use an additional escape - * sequence. However multi line editing is disabled by default. - * - * CUU (Cursor Up) - * Sequence: ESC [ n A - * Effect: moves cursor up of n chars. - * - * CUD (Cursor Down) - * Sequence: ESC [ n B - * Effect: moves cursor down of n chars. - * - * When linenoiseClearScreen() is called, two additional escape sequences - * are used in order to clear the screen and position the cursor at home - * position. - * - * CUP (Cursor position) - * Sequence: ESC [ H - * Effect: moves the cursor to upper left corner - * - * ED (Erase display) - * Sequence: ESC [ 2 J - * Effect: clear the whole screen - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "linenoise.h" - -#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 -#define LINENOISE_MAX_LINE 4096 -#define UNUSED(x) (void)(x) -static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; -static linenoiseCompletionCallback *completionCallback = NULL; -static linenoiseHintsCallback *hintsCallback = NULL; -static linenoiseFreeHintsCallback *freeHintsCallback = NULL; - -static struct termios orig_termios; /* In order to restore at exit.*/ -static int rawmode = 0; /* For atexit() function to check if restore is needed*/ -static int mlmode = 0; /* Multi line mode. Default is single line. */ -static int atexit_registered = 0; /* Register atexit just 1 time. */ -static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; -static int history_len = 0; -static char **history = NULL; - -/* The linenoiseState structure represents the state during line editing. - * We pass this state to functions implementing specific editing - * functionalities. */ -struct linenoiseState { - int ifd; /* Terminal stdin file descriptor. */ - int ofd; /* Terminal stdout file descriptor. */ - char *buf; /* Edited line buffer. */ - size_t buflen; /* Edited line buffer size. */ - const char *prompt; /* Prompt to display. */ - size_t plen; /* Prompt length. */ - size_t pos; /* Current cursor position. */ - size_t oldcolpos; /* Previous refresh cursor column position. */ - size_t len; /* Current edited line length. */ - size_t cols; /* Number of columns in terminal. */ - size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ - int history_index; /* The history index we are currently editing. */ -}; - -enum KEY_ACTION{ - KEY_NULL = 0, /* NULL */ - CTRL_A = 1, /* Ctrl+a */ - CTRL_B = 2, /* Ctrl-b */ - CTRL_C = 3, /* Ctrl-c */ - CTRL_D = 4, /* Ctrl-d */ - CTRL_E = 5, /* Ctrl-e */ - CTRL_F = 6, /* Ctrl-f */ - CTRL_H = 8, /* Ctrl-h */ - TAB = 9, /* Tab */ - CTRL_K = 11, /* Ctrl+k */ - CTRL_L = 12, /* Ctrl+l */ - ENTER = 13, /* Enter */ - CTRL_N = 14, /* Ctrl-n */ - CTRL_P = 16, /* Ctrl-p */ - CTRL_T = 20, /* Ctrl-t */ - CTRL_U = 21, /* Ctrl+u */ - CTRL_W = 23, /* Ctrl+w */ - ESC = 27, /* Escape */ - BACKSPACE = 127 /* Backspace */ -}; - -static void linenoiseAtExit(void); -int linenoiseHistoryAdd(const char *line); -static void refreshLine(struct linenoiseState *l); - -/* Debugging macro. */ -#if 0 -FILE *lndebug_fp = NULL; -#define lndebug(...) \ - do { \ - if (lndebug_fp == NULL) { \ - lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ - fprintf(lndebug_fp, \ - "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ - (int)l->len,(int)l->pos,(int)l->oldcolpos,plen,rows,rpos, \ - (int)l->maxrows,old_rows); \ - } \ - fprintf(lndebug_fp, ", " __VA_ARGS__); \ - fflush(lndebug_fp); \ - } while (0) -#else -#define lndebug(fmt, ...) -#endif - -/* ========================== Encoding functions ============================= */ - -/* Get byte length and column length of the previous character */ -static size_t defaultPrevCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) { - UNUSED(buf); UNUSED(buf_len); UNUSED(pos); - if (col_len != NULL) *col_len = 1; - return 1; -} - -/* Get byte length and column length of the next character */ -static size_t defaultNextCharLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) { - UNUSED(buf); UNUSED(buf_len); UNUSED(pos); - if (col_len != NULL) *col_len = 1; - return 1; -} - -/* Read bytes of the next character */ -static size_t defaultReadCode(int fd, char *buf, size_t buf_len, int* c) { - if (buf_len < 1) return -1; - int nread = read(fd,&buf[0],1); - if (nread == 1) *c = buf[0]; - return nread; -} - -/* Set default encoding functions */ -static linenoisePrevCharLen *prevCharLen = defaultPrevCharLen; -static linenoiseNextCharLen *nextCharLen = defaultNextCharLen; -static linenoiseReadCode *readCode = defaultReadCode; - -/* Set used defined encoding functions */ -void linenoiseSetEncodingFunctions( - linenoisePrevCharLen *prevCharLenFunc, - linenoiseNextCharLen *nextCharLenFunc, - linenoiseReadCode *readCodeFunc) { - prevCharLen = prevCharLenFunc; - nextCharLen = nextCharLenFunc; - readCode = readCodeFunc; -} - -/* Get column length from begining of buffer to current byte position */ -static size_t columnPos(const char *buf, size_t buf_len, size_t pos) { - size_t ret = 0; - size_t off = 0; - while (off < pos) { - size_t col_len; - size_t len = nextCharLen(buf,buf_len,off,&col_len); - off += len; - ret += col_len; - } - return ret; -} - -/* Get column length from begining of buffer to current byte position for multiline mode*/ -static size_t columnPosForMultiLine(const char *buf, size_t buf_len, size_t pos, size_t cols, size_t ini_pos) { - size_t ret = 0; - size_t colwid = ini_pos; - - size_t off = 0; - while (off < buf_len) { - size_t col_len; - size_t len = nextCharLen(buf,buf_len,off,&col_len); - - int dif = (int)(colwid + col_len) - (int)cols; - if (dif > 0) { - ret += dif; - colwid = col_len; - } else if (dif == 0) { - colwid = 0; - } else { - colwid += col_len; - } - - if (off >= pos) break; - off += len; - ret += col_len; - } - - return ret; -} - -/* ======================= Low level terminal handling ====================== */ - -/* Set if to use or not the multi line mode. */ -void linenoiseSetMultiLine(int ml) { - mlmode = ml; -} - -/* Return true if the terminal name is in the list of terminals we know are - * not able to understand basic escape sequences. */ -static int isUnsupportedTerm(void) { - char *term = getenv("TERM"); - int j; - - if (term == NULL) return 0; - for (j = 0; unsupported_term[j]; j++) - if (!strcasecmp(term,unsupported_term[j])) return 1; - return 0; -} - -/* Raw mode: 1960 magic shit. */ -static int enableRawMode(int fd) { - struct termios raw; - - if (!isatty(STDIN_FILENO)) goto fatal; - if (!atexit_registered) { - atexit(linenoiseAtExit); - atexit_registered = 1; - } - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; - - raw = orig_termios; /* modify the original mode */ - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. - * We want read to return every single byte, without timeout. */ - raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ - - /* put terminal in raw mode after flushing */ - if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; - rawmode = 1; - return 0; - -fatal: - errno = ENOTTY; - return -1; -} - -static void disableRawMode(int fd) { - /* Don't even check the return value as it's too late. */ - if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) - rawmode = 0; -} - -/* Use the ESC [6n escape sequence to query the horizontal cursor position - * and return it. On error -1 is returned, on success the position of the - * cursor. */ -static int getCursorPosition(int ifd, int ofd) { - char buf[32]; - int cols, rows; - unsigned int i = 0; - - /* Report cursor location */ - if (write(ofd, "\x1b[6n", 4) != 4) return -1; - - /* Read the response: ESC [ rows ; cols R */ - while (i < sizeof(buf)-1) { - if (read(ifd,buf+i,1) != 1) break; - if (buf[i] == 'R') break; - i++; - } - buf[i] = '\0'; - - /* Parse it. */ - if (buf[0] != ESC || buf[1] != '[') return -1; - if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; - return cols; -} - -/* Try to get the number of columns in the current terminal, or assume 80 - * if it fails. */ -static int getColumns(int ifd, int ofd) { - struct winsize ws; - - if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { - /* ioctl() failed. Try to query the terminal itself. */ - int start, cols; - - /* Get the initial position so we can restore it later. */ - start = getCursorPosition(ifd,ofd); - if (start == -1) goto failed; - - /* Go to right margin and get position. */ - if (write(ofd,"\x1b[999C",6) != 6) goto failed; - cols = getCursorPosition(ifd,ofd); - if (cols == -1) goto failed; - - /* Restore position. */ - if (cols > start) { - char seq[32]; - snprintf(seq,32,"\x1b[%dD",cols-start); - if (write(ofd,seq,strlen(seq)) == -1) { - /* Can't recover... */ - } - } - return cols; - } else { - return ws.ws_col; - } - -failed: - return 80; -} - -/* Clear the screen. Used to handle ctrl+l */ -void linenoiseClearScreen(void) { - if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { - /* nothing to do, just to avoid warning. */ - } -} - -/* Beep, used for completion when there is nothing to complete or when all - * the choices were already shown. */ -static void linenoiseBeep(void) { - fprintf(stderr, "\x7"); - fflush(stderr); -} - -/* ============================== Completion ================================ */ - -/* Free a list of completion option populated by linenoiseAddCompletion(). */ -static void freeCompletions(linenoiseCompletions *lc) { - size_t i; - for (i = 0; i < lc->len; i++) - free(lc->cvec[i]); - if (lc->cvec != NULL) - free(lc->cvec); -} - -/* This is an helper function for linenoiseEdit() and is called when the - * user types the key in order to complete the string currently in the - * input. - * - * The state of the editing is encapsulated into the pointed linenoiseState - * structure as described in the structure definition. */ -static int completeLine(struct linenoiseState *ls, char *cbuf, size_t cbuf_len, int *c) { - linenoiseCompletions lc = { 0, NULL }; - int nread = 0, nwritten; - *c = 0; - - completionCallback(ls->buf,&lc); - if (lc.len == 0) { - linenoiseBeep(); - } else { - size_t stop = 0, i = 0; - - while(!stop) { - /* Show completion or original buffer */ - if (i < lc.len) { - struct linenoiseState saved = *ls; - - ls->len = ls->pos = strlen(lc.cvec[i]); - ls->buf = lc.cvec[i]; - refreshLine(ls); - ls->len = saved.len; - ls->pos = saved.pos; - ls->buf = saved.buf; - } else { - refreshLine(ls); - } - - nread = readCode(ls->ifd,cbuf,cbuf_len,c); - if (nread <= 0) { - freeCompletions(&lc); - *c = -1; - return nread; - } - - switch(*c) { - case 9: /* tab */ - i = (i+1) % (lc.len+1); - if (i == lc.len) linenoiseBeep(); - break; - case 27: /* escape */ - /* Re-show original buffer */ - if (i < lc.len) refreshLine(ls); - stop = 1; - break; - default: - /* Update buffer and return */ - if (i < lc.len) { - nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); - ls->len = ls->pos = nwritten; - } - stop = 1; - break; - } - } - } - - freeCompletions(&lc); - return nread; -} - -/* Register a callback function to be called for tab-completion. */ -void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { - completionCallback = fn; -} - -/* Register a hits function to be called to show hits to the user at the - * right of the prompt. */ -void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { - hintsCallback = fn; -} - -/* Register a function to free the hints returned by the hints callback - * registered with linenoiseSetHintsCallback(). */ -void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { - freeHintsCallback = fn; -} - -/* This function is used by the callback function registered by the user - * in order to add completion options given the input string when the - * user typed . See the example.c source code for a very easy to - * understand example. */ -void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { - size_t len = strlen(str); - char *copy, **cvec; - - copy = malloc(len+1); - if (copy == NULL) return; - memcpy(copy,str,len+1); - cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); - if (cvec == NULL) { - free(copy); - return; - } - lc->cvec = cvec; - lc->cvec[lc->len++] = copy; -} - -/* =========================== Line editing ================================= */ - -/* We define a very simple "append buffer" structure, that is an heap - * allocated string where we can append to. This is useful in order to - * write all the escape sequences in a buffer and flush them to the standard - * output in a single call, to avoid flickering effects. */ -struct abuf { - char *b; - int len; -}; - -static void abInit(struct abuf *ab) { - ab->b = NULL; - ab->len = 0; -} - -static void abAppend(struct abuf *ab, const char *s, int len) { - char *new = realloc(ab->b,ab->len+len); - - if (new == NULL) return; - memcpy(new+ab->len,s,len); - ab->b = new; - ab->len += len; -} - -static void abFree(struct abuf *ab) { - free(ab->b); -} - -/* Helper of refreshSingleLine() and refreshMultiLine() to show hints - * to the right of the prompt. */ -void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int pcollen) { - char seq[64]; - size_t collen = pcollen+columnPos(l->buf,l->len,l->len); - if (hintsCallback && collen < l->cols) { - int color = -1, bold = 0; - char *hint = hintsCallback(l->buf,&color,&bold); - if (hint) { - int hintlen = strlen(hint); - int hintmaxlen = l->cols-collen; - if (hintlen > hintmaxlen) hintlen = hintmaxlen; - if (bold == 1 && color == -1) color = 37; - if (color != -1 || bold != 0) - snprintf(seq,64,"\033[%d;%d;49m",bold,color); - else - seq[0] = '\0'; - abAppend(ab,seq,strlen(seq)); - abAppend(ab,hint,hintlen); - if (color != -1 || bold != 0) - abAppend(ab,"\033[0m",4); - /* Call the function to free the hint returned. */ - if (freeHintsCallback) freeHintsCallback(hint); - } - } -} - -/* Check if text is an ANSI escape sequence - */ -static int isAnsiEscape(const char *buf, size_t buf_len, size_t* len) { - if (buf_len > 2 && !memcmp("\033[", buf, 2)) { - size_t off = 2; - while (off < buf_len) { - switch (buf[off++]) { - case 'A': case 'B': case 'C': case 'D': case 'E': - case 'F': case 'G': case 'H': case 'J': case 'K': - case 'S': case 'T': case 'f': case 'm': - *len = off; - return 1; - } - } - } - return 0; -} - -/* Get column length of prompt text - */ -static size_t promptTextColumnLen(const char *prompt, size_t plen) { - char buf[LINENOISE_MAX_LINE]; - size_t buf_len = 0; - size_t off = 0; - while (off < plen) { - size_t len; - if (isAnsiEscape(prompt + off, plen - off, &len)) { - off += len; - continue; - } - buf[buf_len++] = prompt[off++]; - } - return columnPos(buf,buf_len,buf_len); -} - -/* Single line low level line refresh. - * - * Rewrite the currently edited line accordingly to the buffer content, - * cursor position, and number of columns of the terminal. */ -static void refreshSingleLine(struct linenoiseState *l) { - char seq[64]; - size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt)); - int fd = l->ofd; - char *buf = l->buf; - size_t len = l->len; - size_t pos = l->pos; - struct abuf ab; - - while((pcollen+columnPos(buf,len,pos)) >= l->cols) { - int chlen = nextCharLen(buf,len,0,NULL); - buf += chlen; - len -= chlen; - pos -= chlen; - } - while (pcollen+columnPos(buf,len,len) > l->cols) { - len -= prevCharLen(buf,len,len,NULL); - } - - abInit(&ab); - /* Cursor to left edge */ - snprintf(seq,64,"\r"); - abAppend(&ab,seq,strlen(seq)); - /* Write the prompt and the current buffer content */ - abAppend(&ab,l->prompt,strlen(l->prompt)); - abAppend(&ab,buf,len); - /* Show hits if any. */ - refreshShowHints(&ab,l,pcollen); - /* Erase to right */ - snprintf(seq,64,"\x1b[0K"); - abAppend(&ab,seq,strlen(seq)); - /* Move cursor to original position. */ - snprintf(seq,64,"\r\x1b[%dC", (int)(columnPos(buf,len,pos)+pcollen)); - abAppend(&ab,seq,strlen(seq)); - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ - abFree(&ab); -} - -/* Multi line low level line refresh. - * - * Rewrite the currently edited line accordingly to the buffer content, - * cursor position, and number of columns of the terminal. */ -static void refreshMultiLine(struct linenoiseState *l) { - char seq[64]; - size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt)); - int colpos = columnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcollen); - int colpos2; /* cursor column position. */ - int rows = (pcollen+colpos+l->cols-1)/l->cols; /* rows used by current buf. */ - int rpos = (pcollen+l->oldcolpos+l->cols)/l->cols; /* cursor relative row. */ - int rpos2; /* rpos after refresh. */ - int col; /* colum position, zero-based. */ - int old_rows = l->maxrows; - int fd = l->ofd, j; - struct abuf ab; - - /* Update maxrows if needed. */ - if (rows > (int)l->maxrows) l->maxrows = rows; - - /* First step: clear all the lines used before. To do so start by - * going to the last row. */ - abInit(&ab); - if (old_rows-rpos > 0) { - lndebug("go down %d", old_rows-rpos); - snprintf(seq,64,"\x1b[%dB", old_rows-rpos); - abAppend(&ab,seq,strlen(seq)); - } - - /* Now for every row clear it, go up. */ - for (j = 0; j < old_rows-1; j++) { - lndebug("clear+up"); - snprintf(seq,64,"\r\x1b[0K\x1b[1A"); - abAppend(&ab,seq,strlen(seq)); - } - - /* Clean the top line. */ - lndebug("clear"); - snprintf(seq,64,"\r\x1b[0K"); - abAppend(&ab,seq,strlen(seq)); - - /* Write the prompt and the current buffer content */ - abAppend(&ab,l->prompt,strlen(l->prompt)); - abAppend(&ab,l->buf,l->len); - - /* Show hits if any. */ - refreshShowHints(&ab,l,pcollen); - - /* Get column length to cursor position */ - colpos2 = columnPosForMultiLine(l->buf,l->len,l->pos,l->cols,pcollen); - - /* If we are at the very end of the screen with our prompt, we need to - * emit a newline and move the prompt to the first column. */ - if (l->pos && - l->pos == l->len && - (colpos2+pcollen) % l->cols == 0) - { - lndebug(""); - abAppend(&ab,"\n",1); - snprintf(seq,64,"\r"); - abAppend(&ab,seq,strlen(seq)); - rows++; - if (rows > (int)l->maxrows) l->maxrows = rows; - } - - /* Move cursor to right position. */ - rpos2 = (pcollen+colpos2+l->cols)/l->cols; /* current cursor relative row. */ - lndebug("rpos2 %d", rpos2); - - /* Go up till we reach the expected positon. */ - if (rows-rpos2 > 0) { - lndebug("go-up %d", rows-rpos2); - snprintf(seq,64,"\x1b[%dA", rows-rpos2); - abAppend(&ab,seq,strlen(seq)); - } - - /* Set column. */ - col = (pcollen + colpos2) % l->cols; - lndebug("set col %d", 1+col); - if (col) - snprintf(seq,64,"\r\x1b[%dC", col); - else - snprintf(seq,64,"\r"); - abAppend(&ab,seq,strlen(seq)); - - lndebug("\n"); - l->oldcolpos = colpos2; - - if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ - abFree(&ab); -} - -/* Calls the two low level functions refreshSingleLine() or - * refreshMultiLine() according to the selected mode. */ -static void refreshLine(struct linenoiseState *l) { - if (mlmode) - refreshMultiLine(l); - else - refreshSingleLine(l); -} - -/* Insert the character 'c' at cursor current position. - * - * On error writing to the terminal -1 is returned, otherwise 0. */ -int linenoiseEditInsert(struct linenoiseState *l, const char *cbuf, int clen) { - if (l->len+clen <= l->buflen) { - if (l->len == l->pos) { - memcpy(&l->buf[l->pos],cbuf,clen); - l->pos+=clen; - l->len+=clen;; - l->buf[l->len] = '\0'; - if ((!mlmode && promptTextColumnLen(l->prompt,l->plen)+columnPos(l->buf,l->len,l->len) < l->cols && !hintsCallback)) { - /* Avoid a full update of the line in the - * trivial case. */ - if (write(l->ofd,cbuf,clen) == -1) return -1; - } else { - refreshLine(l); - } - } else { - memmove(l->buf+l->pos+clen,l->buf+l->pos,l->len-l->pos); - memcpy(&l->buf[l->pos],cbuf,clen); - l->pos+=clen; - l->len+=clen; - l->buf[l->len] = '\0'; - refreshLine(l); - } - } - return 0; -} - -/* Move cursor on the left. */ -void linenoiseEditMoveLeft(struct linenoiseState *l) { - if (l->pos > 0) { - l->pos -= prevCharLen(l->buf,l->len,l->pos,NULL); - refreshLine(l); - } -} - -/* Move cursor on the right. */ -void linenoiseEditMoveRight(struct linenoiseState *l) { - if (l->pos != l->len) { - l->pos += nextCharLen(l->buf,l->len,l->pos,NULL); - refreshLine(l); - } -} - -/* Move cursor to the start of the line. */ -void linenoiseEditMoveHome(struct linenoiseState *l) { - if (l->pos != 0) { - l->pos = 0; - refreshLine(l); - } -} - -/* Move cursor to the end of the line. */ -void linenoiseEditMoveEnd(struct linenoiseState *l) { - if (l->pos != l->len) { - l->pos = l->len; - refreshLine(l); - } -} - -/* Substitute the currently edited line with the next or previous history - * entry as specified by 'dir'. */ -#define LINENOISE_HISTORY_NEXT 0 -#define LINENOISE_HISTORY_PREV 1 -void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { - if (history_len > 1) { - /* Update the current history entry before to - * overwrite it with the next one. */ - free(history[history_len - 1 - l->history_index]); - history[history_len - 1 - l->history_index] = strdup(l->buf); - /* Show the new entry */ - l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; - if (l->history_index < 0) { - l->history_index = 0; - return; - } else if (l->history_index >= history_len) { - l->history_index = history_len-1; - return; - } - strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); - l->buf[l->buflen-1] = '\0'; - l->len = l->pos = strlen(l->buf); - refreshLine(l); - } -} - -/* Delete the character at the right of the cursor without altering the cursor - * position. Basically this is what happens with the "Delete" keyboard key. */ -void linenoiseEditDelete(struct linenoiseState *l) { - if (l->len > 0 && l->pos < l->len) { - int chlen = nextCharLen(l->buf,l->len,l->pos,NULL); - memmove(l->buf+l->pos,l->buf+l->pos+chlen,l->len-l->pos-chlen); - l->len-=chlen; - l->buf[l->len] = '\0'; - refreshLine(l); - } -} - -/* Backspace implementation. */ -void linenoiseEditBackspace(struct linenoiseState *l) { - if (l->pos > 0 && l->len > 0) { - int chlen = prevCharLen(l->buf,l->len,l->pos,NULL); - memmove(l->buf+l->pos-chlen,l->buf+l->pos,l->len-l->pos); - l->pos-=chlen; - l->len-=chlen; - l->buf[l->len] = '\0'; - refreshLine(l); - } -} - -/* Delete the previosu word, maintaining the cursor at the start of the - * current word. */ -void linenoiseEditDeletePrevWord(struct linenoiseState *l) { - size_t old_pos = l->pos; - size_t diff; - - while (l->pos > 0 && l->buf[l->pos-1] == ' ') - l->pos--; - while (l->pos > 0 && l->buf[l->pos-1] != ' ') - l->pos--; - diff = old_pos - l->pos; - memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); - l->len -= diff; - refreshLine(l); -} - -/* This function is the core of the line editing capability of linenoise. - * It expects 'fd' to be already in "raw mode" so that every key pressed - * will be returned ASAP to read(). - * - * The resulting string is put into 'buf' when the user type enter, or - * when ctrl+d is typed. - * - * The function returns the length of the current buffer. */ -static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) -{ - struct linenoiseState l; - - /* Populate the linenoise state that we pass to functions implementing - * specific editing functionalities. */ - l.ifd = stdin_fd; - l.ofd = stdout_fd; - l.buf = buf; - l.buflen = buflen; - l.prompt = prompt; - l.plen = strlen(prompt); - l.oldcolpos = l.pos = 0; - l.len = 0; - l.cols = getColumns(stdin_fd, stdout_fd); - l.maxrows = 0; - l.history_index = 0; - - /* Buffer starts empty. */ - l.buf[0] = '\0'; - l.buflen--; /* Make sure there is always space for the nulterm */ - - /* The latest history entry is always our current buffer, that - * initially is just an empty string. */ - linenoiseHistoryAdd(""); - - if (write(l.ofd,prompt,l.plen) == -1) return -1; - while(1) { - int c; - char cbuf[32]; // large enough for any encoding? - int nread; - char seq[3]; - - nread = readCode(l.ifd,cbuf,sizeof(cbuf),&c); - if (nread <= 0) return l.len; - - /* Only autocomplete when the callback is set. It returns < 0 when - * there was an error reading from fd. Otherwise it will return the - * character that should be handled next. */ - if (c == 9 && completionCallback != NULL) { - nread = completeLine(&l,cbuf,sizeof(cbuf),&c); - /* Return on errors */ - if (c < 0) return l.len; - /* Read next character when 0 */ - if (c == 0) continue; - } - - switch(c) { - case ENTER: /* enter */ - history_len--; - free(history[history_len]); - if (mlmode) linenoiseEditMoveEnd(&l); - if (hintsCallback) { - /* Force a refresh without hints to leave the previous - * line as the user typed it after a newline. */ - linenoiseHintsCallback *hc = hintsCallback; - hintsCallback = NULL; - refreshLine(&l); - hintsCallback = hc; - } - return (int)l.len; - case CTRL_C: /* ctrl-c */ - errno = EAGAIN; - return -1; - case BACKSPACE: /* backspace */ - case 8: /* ctrl-h */ - linenoiseEditBackspace(&l); - break; - case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the - line is empty, act as end-of-file. */ - if (l.len > 0) { - linenoiseEditDelete(&l); - } else { - history_len--; - free(history[history_len]); - return -1; - } - break; - case CTRL_T: /* ctrl-t, swaps current character with previous. */ - if (l.pos > 0 && l.pos < l.len) { - int aux = buf[l.pos-1]; - buf[l.pos-1] = buf[l.pos]; - buf[l.pos] = aux; - if (l.pos != l.len-1) l.pos++; - refreshLine(&l); - } - break; - case CTRL_B: /* ctrl-b */ - linenoiseEditMoveLeft(&l); - break; - case CTRL_F: /* ctrl-f */ - linenoiseEditMoveRight(&l); - break; - case CTRL_P: /* ctrl-p */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); - break; - case CTRL_N: /* ctrl-n */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); - break; - case ESC: /* escape sequence */ - /* Read the next two bytes representing the escape sequence. - * Use two calls to handle slow terminals returning the two - * chars at different times. */ - if (read(l.ifd,seq,1) == -1) break; - if (read(l.ifd,seq+1,1) == -1) break; - - /* ESC [ sequences. */ - if (seq[0] == '[') { - if (seq[1] >= '0' && seq[1] <= '9') { - /* Extended escape, read additional byte. */ - if (read(l.ifd,seq+2,1) == -1) break; - if (seq[2] == '~') { - switch(seq[1]) { - case '3': /* Delete key. */ - linenoiseEditDelete(&l); - break; - } - } - } else { - switch(seq[1]) { - case 'A': /* Up */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); - break; - case 'B': /* Down */ - linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); - break; - case 'C': /* Right */ - linenoiseEditMoveRight(&l); - break; - case 'D': /* Left */ - linenoiseEditMoveLeft(&l); - break; - case 'H': /* Home */ - linenoiseEditMoveHome(&l); - break; - case 'F': /* End*/ - linenoiseEditMoveEnd(&l); - break; - } - } - } - - /* ESC O sequences. */ - else if (seq[0] == 'O') { - switch(seq[1]) { - case 'H': /* Home */ - linenoiseEditMoveHome(&l); - break; - case 'F': /* End*/ - linenoiseEditMoveEnd(&l); - break; - } - } - break; - default: - if (linenoiseEditInsert(&l,cbuf,nread)) return -1; - break; - case CTRL_U: /* Ctrl+u, delete the whole line. */ - buf[0] = '\0'; - l.pos = l.len = 0; - refreshLine(&l); - break; - case CTRL_K: /* Ctrl+k, delete from current to end of line. */ - buf[l.pos] = '\0'; - l.len = l.pos; - refreshLine(&l); - break; - case CTRL_A: /* Ctrl+a, go to the start of the line */ - linenoiseEditMoveHome(&l); - break; - case CTRL_E: /* ctrl+e, go to the end of the line */ - linenoiseEditMoveEnd(&l); - break; - case CTRL_L: /* ctrl+l, clear screen */ - linenoiseClearScreen(); - refreshLine(&l); - break; - case CTRL_W: /* ctrl+w, delete previous word */ - linenoiseEditDeletePrevWord(&l); - break; - } - } - return l.len; -} - -/* This special mode is used by linenoise in order to print scan codes - * on screen for debugging / development purposes. It is implemented - * by the linenoise_example program using the --keycodes option. */ -void linenoisePrintKeyCodes(void) { - char quit[4]; - - printf("Linenoise key codes debugging mode.\n" - "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); - if (enableRawMode(STDIN_FILENO) == -1) return; - memset(quit,' ',4); - while(1) { - char c; - int nread; - - nread = read(STDIN_FILENO,&c,1); - if (nread <= 0) continue; - memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ - quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ - if (memcmp(quit,"quit",sizeof(quit)) == 0) break; - - printf("'%c' %02x (%d) (type quit to exit)\n", - isprint((int)c) ? c : '?', (int)c, (int)c); - printf("\r"); /* Go left edge manually, we are in raw mode. */ - fflush(stdout); - } - disableRawMode(STDIN_FILENO); -} - -/* This function calls the line editing function linenoiseEdit() using - * the STDIN file descriptor set in raw mode. */ -static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { - int count; - - if (buflen == 0) { - errno = EINVAL; - return -1; - } - - if (enableRawMode(STDIN_FILENO) == -1) return -1; - count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); - disableRawMode(STDIN_FILENO); - printf("\n"); - return count; -} - -/* This function is called when linenoise() is called with the standard - * input file descriptor not attached to a TTY. So for example when the - * program using linenoise is called in pipe or with a file redirected - * to its standard input. In this case, we want to be able to return the - * line regardless of its length (by default we are limited to 4k). */ -static char *linenoiseNoTTY(void) { - char *line = NULL; - size_t len = 0, maxlen = 0; - - while(1) { - if (len == maxlen) { - if (maxlen == 0) maxlen = 16; - maxlen *= 2; - char *oldval = line; - line = realloc(line,maxlen); - if (line == NULL) { - if (oldval) free(oldval); - return NULL; - } - } - int c = fgetc(stdin); - if (c == EOF || c == '\n') { - if (c == EOF && len == 0) { - free(line); - return NULL; - } else { - line[len] = '\0'; - return line; - } - } else { - line[len] = c; - len++; - } - } -} - -/* The high level function that is the main API of the linenoise library. - * This function checks if the terminal has basic capabilities, just checking - * for a blacklist of stupid terminals, and later either calls the line - * editing function or uses dummy fgets() so that you will be able to type - * something even in the most desperate of the conditions. */ -char *linenoise(const char *prompt) { - char buf[LINENOISE_MAX_LINE]; - int count; - - if (!isatty(STDIN_FILENO)) { - /* Not a tty: read from file / pipe. In this mode we don't want any - * limit to the line size, so we call a function to handle that. */ - return linenoiseNoTTY(); - } else if (isUnsupportedTerm()) { - size_t len; - - printf("%s",prompt); - fflush(stdout); - if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; - len = strlen(buf); - while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { - len--; - buf[len] = '\0'; - } - return strdup(buf); - } else { - count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); - if (count == -1) return NULL; - return strdup(buf); - } -} - -/* This is just a wrapper the user may want to call in order to make sure - * the linenoise returned buffer is freed with the same allocator it was - * created with. Useful when the main program is using an alternative - * allocator. */ -void linenoiseFree(void *ptr) { - free(ptr); -} - -/* ================================ History ================================= */ - -/* Free the history, but does not reset it. Only used when we have to - * exit() to avoid memory leaks are reported by valgrind & co. */ -static void freeHistory(void) { - if (history) { - int j; - - for (j = 0; j < history_len; j++) - free(history[j]); - free(history); - } -} - -/* At exit we'll try to fix the terminal to the initial conditions. */ -static void linenoiseAtExit(void) { - disableRawMode(STDIN_FILENO); - freeHistory(); -} - -/* This is the API call to add a new entry in the linenoise history. - * It uses a fixed array of char pointers that are shifted (memmoved) - * when the history max length is reached in order to remove the older - * entry and make room for the new one, so it is not exactly suitable for huge - * histories, but will work well for a few hundred of entries. - * - * Using a circular buffer is smarter, but a bit more complex to handle. */ -int linenoiseHistoryAdd(const char *line) { - char *linecopy; - - if (history_max_len == 0) return 0; - - /* Initialization on first call. */ - if (history == NULL) { - history = malloc(sizeof(char*)*history_max_len); - if (history == NULL) return 0; - memset(history,0,(sizeof(char*)*history_max_len)); - } - - /* Don't add duplicated lines. */ - if (history_len && !strcmp(history[history_len-1], line)) return 0; - - /* Add an heap allocated copy of the line in the history. - * If we reached the max length, remove the older line. */ - linecopy = strdup(line); - if (!linecopy) return 0; - if (history_len == history_max_len) { - free(history[0]); - memmove(history,history+1,sizeof(char*)*(history_max_len-1)); - history_len--; - } - history[history_len] = linecopy; - history_len++; - return 1; -} - -/* Set the maximum length for the history. This function can be called even - * if there is already some history, the function will make sure to retain - * just the latest 'len' elements if the new history length value is smaller - * than the amount of items already inside the history. */ -int linenoiseHistorySetMaxLen(int len) { - char **new; - - if (len < 1) return 0; - if (history) { - int tocopy = history_len; - - new = malloc(sizeof(char*)*len); - if (new == NULL) return 0; - - /* If we can't copy everything, free the elements we'll not use. */ - if (len < tocopy) { - int j; - - for (j = 0; j < tocopy-len; j++) free(history[j]); - tocopy = len; - } - memset(new,0,sizeof(char*)*len); - memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); - free(history); - history = new; - } - history_max_len = len; - if (history_len > history_max_len) - history_len = history_max_len; - return 1; -} - -/* Save the history in the specified file. On success 0 is returned - * otherwise -1 is returned. */ -int linenoiseHistorySave(const char *filename) { - mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - FILE *fp; - int j; - - fp = fopen(filename,"w"); - umask(old_umask); - if (fp == NULL) return -1; - chmod(filename,S_IRUSR|S_IWUSR); - for (j = 0; j < history_len; j++) - fprintf(fp,"%s\n",history[j]); - fclose(fp); - return 0; -} - -/* Load the history from the specified file. If the file does not exist - * zero is returned and no operation is performed. - * - * If the file exists and the operation succeeded 0 is returned, otherwise - * on error -1 is returned. */ -int linenoiseHistoryLoad(const char *filename) { - FILE *fp = fopen(filename,"r"); - char buf[LINENOISE_MAX_LINE]; - - if (fp == NULL) return -1; - - while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { - char *p; - - p = strchr(buf,'\r'); - if (!p) p = strchr(buf,'\n'); - if (p) *p = '\0'; - linenoiseHistoryAdd(buf); - } - fclose(fp); - return 0; -} diff --git a/external/linenoise/linenoise.h b/external/linenoise/linenoise.h deleted file mode 100644 index df32133c..00000000 --- a/external/linenoise/linenoise.h +++ /dev/null @@ -1,82 +0,0 @@ -/* linenoise.h -- VERSION 1.0 - * - * Guerrilla line editing library against the idea that a line editing lib - * needs to be 20,000 lines of C code. - * - * See linenoise.c for more information. - * - * ------------------------------------------------------------------------ - * - * Copyright (c) 2010-2014, Salvatore Sanfilippo - * Copyright (c) 2010-2013, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __LINENOISE_H -#define __LINENOISE_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct linenoiseCompletions { - size_t len; - char **cvec; -} linenoiseCompletions; - -typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); -typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); -typedef void(linenoiseFreeHintsCallback)(void *); -void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); -void linenoiseSetHintsCallback(linenoiseHintsCallback *); -void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); -void linenoiseAddCompletion(linenoiseCompletions *, const char *); - -char *linenoise(const char *prompt); -void linenoiseFree(void *ptr); -int linenoiseHistoryAdd(const char *line); -int linenoiseHistorySetMaxLen(int len); -int linenoiseHistorySave(const char *filename); -int linenoiseHistoryLoad(const char *filename); -void linenoiseClearScreen(void); -void linenoiseSetMultiLine(int ml); -void linenoisePrintKeyCodes(void); - -typedef size_t (linenoisePrevCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len); -typedef size_t (linenoiseNextCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len); -typedef size_t (linenoiseReadCode)(int fd, char *buf, size_t buf_len, int* c); - -void linenoiseSetEncodingFunctions( - linenoisePrevCharLen *prevCharLenFunc, - linenoiseNextCharLen *nextCharLenFunc, - linenoiseReadCode *readCodeFunc); - -#ifdef __cplusplus -} -#endif - -#endif /* __LINENOISE_H */ diff --git a/external/linenoise/makefile b/external/linenoise/makefile deleted file mode 100644 index 7993ffcf..00000000 --- a/external/linenoise/makefile +++ /dev/null @@ -1,20 +0,0 @@ -# bleugh. -# so we can call from our main makefile. - - -.PHONY: all - -all: ../liblinenoise.a - @: - -../liblinenoise.a: linenoise.c.o encodings/utf8.c.o - @echo "# liblinenoise" - @$(AR) rcs ../liblinenoise.a linenoise.c.o encodings/utf8.c.o - -%.c.o: %.c - @echo "# linenoise/$(notdir $<)" - @$(CC) -std=c11 -O3 -c -o $@ $< - - - - diff --git a/makefile b/makefile index b793a66c..1eb4b12a 100644 --- a/makefile +++ b/makefile @@ -85,8 +85,6 @@ endif UTF8REWIND_AR := external/libutf8rewind.a -LINENOISE_AR := external/liblinenoise.a -REPLXX_AR := external/libreplxx.a FLXFLAGS += -sysroot $(SYSROOT) --ffi-escape @@ -146,10 +144,10 @@ copylibs: $(FLXSRC) @mv $(FLXLIBLOCATION)/libs $(FLXLIBLOCATION)/flaxlibs -$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(LINENOISE_AR) $(REPLXX_AR) +$(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) @printf "# linking\n" @mkdir -p $(dir $(OUTPUT)) - @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind -llinenoise -lreplxx + @$(CXX) -o $@ $(CXXOBJ) $(COBJ) $(LDFLAGS) -Lexternal -L$(shell $(LLVM_CONFIG) --prefix)/lib $(shell $(LLVM_CONFIG) --system-libs --libs core engine native linker bitwriter lto vectorize all-targets object orcjit) -lmpfr -lgmp -lpthread -ldl -lffi -lutf8rewind %.cpp.o: %.cpp @@ -165,12 +163,6 @@ $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(LINENOISE_AR) $(R $(UTF8REWIND_AR): @make -C external/utf8rewind all -$(LINENOISE_AR): - @make -C external/linenoise all - -$(REPLXX_AR): $(shell find external/replxx -name "*.cpp" -or -name "*.hxx") - @make -C external/replxx all - # haha clena: clean clean: diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index cb9cdb35..4fbd9687 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -322,7 +322,7 @@ namespace fir std::string ConstantCharSlice::str() { - return this->value; + return zpr::sprint("\"%s\"", this->value); } diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 8076681e..bb2d45b7 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -163,7 +163,7 @@ namespace detail -#if ZTMU_CREATE_IMPL || true +#if ZTMU_CREATE_IMPL // comes as a pair yo #include "zpr.h" diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 3c10f602..8fc9656c 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -10,8 +10,6 @@ #define ZTMU_CREATE_IMPL 1 #include "ztmu.h" -#include "replxx/include/replxx.hxx" - namespace repl { static void runCommand(const std::string& s) diff --git a/source/repl/parse.cpp b/source/repl/execute.cpp similarity index 75% rename from source/repl/parse.cpp rename to source/repl/execute.cpp index 3a4c786a..fe4d935b 100644 --- a/source/repl/parse.cpp +++ b/source/repl/execute.cpp @@ -1,4 +1,4 @@ -// parse.cpp +// execute.cpp // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. @@ -44,7 +44,8 @@ namespace repl sst::TypecheckState* fs; cgn::CodegenState* cs; - size_t counter = 0; + size_t fnCounter = 0; + size_t varCounter = 0; }; static State* state = 0; @@ -96,10 +97,25 @@ namespace repl // ok, we have a thing. try to run it. auto value = magicallyRunExpressionAtCompileTime(state->cs, tcr.stmt(), nullptr, - Identifier(zpr::sprint("__anon_runner_%d", state->counter++), IdKind::Name)); + Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name)); if(value) - printf("%s\n", zpr::sprint("%s :: %s", value->str(), value->getType()).c_str()); + { + // if it was an expression, then give it a name so we can refer to it later. + auto init = util::pool(Location(), value->getType()); + init->rawValue = CGResult(value); + + auto vardef = util::pool(Location()); + vardef->type = init->type; + vardef->id = Identifier(zpr::sprint("_%d", state->varCounter++), IdKind::Name); + vardef->global = true; + vardef->init = init; + + state->fs->stree->addDefinition(vardef->id.name, vardef); + + + printf("%s\n", zpr::sprint("%s: %s = %s", vardef->id.name, value->getType(), value->str()).c_str()); + } } } From 988f373b9e004c46cbc3c4dd85f9a3752054b03f Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 12 Nov 2019 01:17:09 +0800 Subject: [PATCH 066/129] fix a massive bug in TCResult --- source/include/defs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/include/defs.h b/source/include/defs.h index 3c2ad42f..4db8295c 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -491,6 +491,8 @@ struct TCResult { if(&r != this) { + this->_kind = r._kind; + if(this->isError()) { this->_pe = r._pe; r._pe = 0; } else if(this->isStmt()) { this->_st = r._st; r._st = 0; } else if(this->isExpr()) { this->_ex = r._ex; r._ex = 0; } From 2dda3169da71788765f53a162365348a084d258f Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 12 Nov 2019 01:17:09 +0800 Subject: [PATCH 067/129] fix a massive bug in TCResult --- source/include/defs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/include/defs.h b/source/include/defs.h index cb831bd0..979216b1 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -480,6 +480,8 @@ struct TCResult { if(&r != this) { + this->_kind = r._kind; + if(this->isError()) { this->_pe = r._pe; r._pe = 0; } else if(this->isStmt()) { this->_st = r._st; r._st = 0; } else if(this->isExpr()) { this->_ex = r._ex; r._ex = 0; } From 7aa596f4418777baabe11819a9c45a5bdc2bfc38 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 12 Nov 2019 22:47:51 +0800 Subject: [PATCH 068/129] lots of stuff, enable pulling out more complex values from the interpreter --- build/supertiny.flx | 7 ++ makefile | 2 +- meson.build | 3 + source/backend/llvm/translator.cpp | 29 ++++-- source/codegen/call.cpp | 4 +- source/codegen/codegenstate.cpp | 30 +++--- source/codegen/directives.cpp | 30 +++++- source/codegen/raii.cpp | 8 ++ source/codegen/refcounting.cpp | 6 ++ source/fir/ConstantValue.cpp | 26 ++++- source/fir/Types/ArraySliceType.cpp | 6 ++ source/fir/interp/interpreter.cpp | 128 +++++++++++++++++++++---- source/frontend/parser/controlflow.cpp | 16 +++- source/include/codegen.h | 5 +- source/include/defs.h | 7 ++ source/include/ir/constant.h | 17 ++++ source/include/ir/type.h | 77 ++++++++------- source/include/parser_internal.h | 4 +- source/include/zpr.h | 26 +++++ source/include/ztmu.h | 126 ++++++++++++++++-------- source/misc/identifier.cpp | 6 +- source/platform/backtrace.cpp | 2 +- source/repl/driver.cpp | 53 ++++++---- source/repl/execute.cpp | 39 +++++++- source/typecheck/directives.cpp | 3 +- source/typecheck/toplevel.cpp | 16 +++- source/typecheck/variable.cpp | 8 +- 27 files changed, 517 insertions(+), 167 deletions(-) diff --git a/build/supertiny.flx b/build/supertiny.flx index e256c9d6..0a8cd055 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -36,6 +36,9 @@ struct foo x: int } +let glob = "foozle"[2:] +let glob2 = string("lmao") + @entry fn main() { // do { @@ -55,6 +58,10 @@ struct foo printf("%d, %d\n", x, y) } + do { + printf("%s\n", glob) + } + // fn foo(a: T) -> T // { // return a diff --git a/makefile b/makefile index 1eb4b12a..7f458ea4 100644 --- a/makefile +++ b/makefile @@ -39,7 +39,7 @@ NUMFILES := $$(($(words $(CXXSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++17 -fvisibility=hidden -O0 -g -c -Wall -frtti -fno-exceptions -fno-omit-frame-pointer $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -fvisibility=hidden -O0 -g -c -Wall -frtti -fno-omit-frame-pointer $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) -fvisibility=hidden diff --git a/meson.build b/meson.build index 89383f72..9b0f85f8 100644 --- a/meson.build +++ b/meson.build @@ -186,6 +186,9 @@ source_files = files([ 'source/misc/identifier.cpp', 'source/misc/destructors.cpp', + 'source/repl/driver.cpp', + 'source/repl/execute.cpp', + 'source/frontend/pts.cpp', 'source/frontend/file.cpp', 'source/frontend/lexer.cpp', diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 094a2860..9e91e805 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -457,26 +457,43 @@ namespace backend return cachedConstants[fc] = llvm::ConstantStruct::get(llvm::cast(ty), constToLlvm(cec->getIndex(), valueMap, mod), constToLlvm(cec->getValue(), valueMap, mod)); } - else if(auto cs = dcast(fir::ConstantCharSlice, fc)) + else if(dcast(fir::ConstantCharSlice, fc) || dcast(fir::ConstantDynamicString, fc)) { - size_t origLen = cs->getValue().length(); - std::string str = cs->getValue(); + bool wasDynStr = false; + + std::string str; + if(auto ccs = dcast(fir::ConstantCharSlice, fc)) + str = ccs->getValue(); + + else if(auto cds = dcast(fir::ConstantDynamicString, fc)) + wasDynStr = true, str = cds->getValue(); + llvm::Constant* cstr = llvm::ConstantDataArray::getString(LLVMBackend::getLLVMContext(), str, true); llvm::GlobalVariable* gv = new llvm::GlobalVariable(*mod, cstr->getType(), true, - llvm::GlobalValue::LinkageTypes::InternalLinkage, cstr, "_FV_STR_" + std::to_string(cs->id)); + llvm::GlobalValue::LinkageTypes::InternalLinkage, cstr, "_FV_STR_" + std::to_string(fc->id)); auto zconst = llvm::ConstantInt::get(getNativeWordTy(), 0); std::vector indices = { zconst, zconst }; llvm::Constant* gepd = llvm::ConstantExpr::getGetElementPtr(gv->getType()->getPointerElementType(), gv, indices); - auto len = llvm::ConstantInt::get(getNativeWordTy(), origLen); + auto len = llvm::ConstantInt::get(getNativeWordTy(), str.size()); iceAssert(gepd->getType() == llvm::Type::getInt8PtrTy(LLVMBackend::getLLVMContext())); iceAssert(len->getType() == getNativeWordTy()); + fir::Type* ty = fir::Type::getCharSlice(false); std::vector mems = { gepd, len }; - auto ret = llvm::ConstantStruct::get(llvm::cast(typeToLlvm(fir::Type::getCharSlice(false), mod)), mems); + if(wasDynStr) + { + ty = fir::Type::getString(); + + // add -1 for the capacity and 0 for the refcountptr. + mems.push_back(llvm::ConstantInt::get(getNativeWordTy(), -1)); + mems.push_back(zconst); + } + + auto ret = llvm::ConstantStruct::get(llvm::cast(typeToLlvm(ty, mod)), mems); cachedConstants[fc] = ret; return ret; diff --git a/source/codegen/call.cpp b/source/codegen/call.cpp index ecede101..21321ddc 100644 --- a/source/codegen/call.cpp +++ b/source/codegen/call.cpp @@ -400,7 +400,9 @@ static CGResult callBuiltinTypeConstructor(cgn::CodegenState* cs, fir::Type* typ auto len = cs->oneWayAutocast(args[1]->codegen(cs, fir::Type::getNativeWord()).value, fir::Type::getNativeWord()); auto slc = cs->irb.CreateValue(fir::Type::getCharSlice(false)); - slc = cs->irb.SetArraySliceData(slc, (ptr->getType()->isMutablePointer() ? cs->irb.PointerTypeCast(ptr, fir::Type::getInt8Ptr()) : ptr)); + slc = cs->irb.SetArraySliceData(slc, (ptr->getType()->isMutablePointer() + ? cs->irb.PointerTypeCast(ptr, fir::Type::getInt8Ptr()) : ptr)); + slc = cs->irb.SetArraySliceLength(slc, len); return cloneTheSlice(slc); diff --git a/source/codegen/codegenstate.cpp b/source/codegen/codegenstate.cpp index 2802c06f..28c38acb 100644 --- a/source/codegen/codegenstate.cpp +++ b/source/codegen/codegenstate.cpp @@ -333,23 +333,12 @@ namespace cgn - void CodegenState::addGlobalInitialiser(fir::Value* storage, fir::Value* value) - { - if(!storage->getType()->isPointerType()) - { - error(this->loc(), "'storage' must be pointer type, got '%s'", storage->getType()); - } - else if(storage->getType()->getPointerElementType() != value->getType()) - { - error(this->loc(), "cannot store value of type '%s' into storage of type '%s'", value->getType(), - storage->getType()); - } - // ok, then - this->globalInits.push_back({ value, storage }); + bool CodegenState::isWithinGlobalInitFunction() + { + return this->isInsideGlobalInitFunc; } - fir::IRBlock* CodegenState::enterGlobalInitFunction() { auto ret = this->irb.getCurrentBlock(); @@ -368,19 +357,30 @@ namespace cgn iceAssert(this->globalInitFunc); this->irb.setCurrentBlock(this->globalInitFunc->getBlockList().back()); + this->isInsideGlobalInitFunc = true; return ret; } void CodegenState::leaveGlobalInitFunction(fir::IRBlock* restore) { this->irb.setCurrentBlock(restore); + this->isInsideGlobalInitFunc = false; } void CodegenState::finishGlobalInitFunction() { auto r = this->enterGlobalInitFunction(); - this->irb.ReturnVoid(); + // ok, here's a thing -- if the current block doesn't end with a return, then we insert it. + // this lets us keep calling finishGlobalInitFunction() without repercussions, even if we + // already "finished" the constructor function. this workaround is used to make our life + // simpler when doing compile-time execution. + + if(const auto& instrs = this->irb.getCurrentBlock()->getInstructions(); + instrs.empty() || instrs.back()->opKind != fir::OpKind::Value_Return) + { + this->irb.ReturnVoid(); + } this->leaveGlobalInitFunction(r); } diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp index b69e80e4..c630af4b 100644 --- a/source/codegen/directives.cpp +++ b/source/codegen/directives.cpp @@ -8,8 +8,10 @@ #include "ir/interp.h" +#include "memorypool.h" -fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, const Identifier& fname) +fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, + const Identifier& fname, fir::interp::InterpState* is = 0) { // what we do is to make a new function in IR, set the insertpoint to that, // then run codegen on the expression (so it generates inside), restore the insertpoint, @@ -34,6 +36,11 @@ fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, s auto entry = cs->irb.addNewBlockInFunction("entry", fn); cs->irb.setCurrentBlock(entry); + // we need a block, even though we don't codegen -- this is to handle raii/refcounting things. + // stack allocate, so we can get rid of it later. (automatically) + auto fakeBlk = sst::Block(Location()); + cs->enterBlock(&fakeBlk); + fir::Value* ret = 0; if(isExpr) ret = stmt->codegen(cs, infer).value; @@ -45,21 +52,36 @@ fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, s else cs->irb.Return(ret); + + cs->leaveBlock(); + if(restore) cs->irb.setCurrentBlock(restore); } + // finalise the global init function if necessary: + cs->finishGlobalInitFunction(); + // run the function: fir::ConstantValue* ret = 0; { - auto is = new fir::interp::InterpState(cs->module); - is->initialise(); + bool needToFinalise = false; + if(!is) + { + is = new fir::interp::InterpState(cs->module); + is->initialise(); + + needToFinalise = true; + } + { auto result = is->runFunction(is->compileFunction(fn), { }); if(!retty->isVoidType()) ret = is->unwrapInterpValueIntoConstant(result); } - is->finalise(); + + if(needToFinalise) + is->finalise(); delete is; } diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index bfa943eb..d0a17c97 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -10,6 +10,10 @@ namespace cgn { void CodegenState::addRAIIValue(fir::Value* val) { + // TODO: we need global destructors eventually. + if(this->isWithinGlobalInitFunction()) + return; + if(!this->isRAIIType(val->getType())) error("val is not a class type! '%s'", val->getType()); @@ -26,6 +30,10 @@ namespace cgn void CodegenState::removeRAIIValue(fir::Value* val) { + // TODO: we need global destructors eventually. + if(this->isWithinGlobalInitFunction()) + return; + if(!this->isRAIIType(val->getType())) error("val is not a class type! '%s'", val->getType()); diff --git a/source/codegen/refcounting.cpp b/source/codegen/refcounting.cpp index 04be8427..ac3a83b3 100644 --- a/source/codegen/refcounting.cpp +++ b/source/codegen/refcounting.cpp @@ -10,6 +10,9 @@ namespace cgn { void CodegenState::addRefCountedValue(fir::Value* val) { + if(this->isWithinGlobalInitFunction()) + return; + auto list = &this->blockPointStack.back().refCountedValues; if(auto it = std::find(list->begin(), list->end(), val); it == list->end()) @@ -25,6 +28,9 @@ namespace cgn void CodegenState::removeRefCountedValue(fir::Value* val) { + if(this->isWithinGlobalInitFunction()) + return; + auto list = &this->blockPointStack.back().refCountedValues; if(auto it = std::find(list->begin(), list->end(), val); it != list->end()) diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 4fbd9687..1e1b8a0d 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -358,7 +358,7 @@ namespace fir std::string ConstantTuple::str() { - return "(" + util::listToString(this->values, [](auto x) -> auto { return x->str(); }) + ")"; + return "(" + util::join(util::map(this->values, [](auto x) -> auto { return x->str(); }), ", ") + ")"; } @@ -433,7 +433,7 @@ namespace fir std::string ConstantArray::str() { - return "[ " + util::listToString(this->values, [](auto x) -> auto { return x->str(); }) + " ]"; + return "[ " + util::join(util::map(this->values, [](auto x) -> auto { return x->str(); }), ", ") + " ]"; } @@ -470,6 +470,28 @@ namespace fir + ConstantDynamicString* ConstantDynamicString::get(const std::string& s) + { + return new ConstantDynamicString(s); + } + + ConstantDynamicString::ConstantDynamicString(const std::string& s) : ConstantValue(fir::Type::getString()) + { + this->value = s; + } + + std::string ConstantDynamicString::getValue() + { + return this->value; + } + + std::string ConstantDynamicString::str() + { + return zpr::sprint("\"%s\"", this->value); + } + + + diff --git a/source/fir/Types/ArraySliceType.cpp b/source/fir/Types/ArraySliceType.cpp index 88dbd6ff..d8bfe4a0 100644 --- a/source/fir/Types/ArraySliceType.cpp +++ b/source/fir/Types/ArraySliceType.cpp @@ -25,6 +25,12 @@ namespace fir return this->isSliceMutable; } + Type* ArraySliceType::getDataPointerType() + { + if(this->isSliceMutable) return this->arrayElementType->getMutablePointerTo(); + else return this->arrayElementType->getPointerTo(); + } + std::string ArraySliceType::str() { if(this->isCharSliceType()) diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 277d2564..3bef5bb3 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -290,7 +290,7 @@ namespace interp auto ptr = fir::ConstantBitcast::get(fir::ConstantInt::getUNative(reinterpret_cast(str)), fir::Type::getInt8Ptr()); auto len = fir::ConstantInt::getNative(cs->getValue().size()); - auto bytecount = getSizeOfType(ptr->getType()) + getSizeOfType(len->getType()); + auto bytecount = getSizeOfType(cs->getType()); auto ret = constructStructThingy(cs, bytecount, { ptr, len }); @@ -305,23 +305,21 @@ namespace interp } else if(auto ca = dcast(fir::ConstantArray, c)) { - auto bytecount = ca->getValues().size() * getSizeOfType(ca->getType()->getArrayElementType()); + auto bytecount = getSizeOfType(ca->getType()); auto ret = constructStructThingy(ca, bytecount, ca->getValues()); return (cachedConstants[c] = ret); } else if(auto ct = dcast(fir::ConstantTuple, c)) { - size_t bytecount = 0; - for(auto t : ct->getValues()) - bytecount += getSizeOfType(t->getType()); + size_t bytecount = getSizeOfType(ct->getType()); auto ret = constructStructThingy(ct, bytecount, ct->getValues()); return (cachedConstants[c] = ret); } else if(auto cec = dcast(fir::ConstantEnumCase, c)) { - auto bytecount = getSizeOfType(cec->getIndex()->getType()) + getSizeOfType(cec->getValue()->getType()); + auto bytecount = getSizeOfType(cec->getType()); auto ret = constructStructThingy(cec, bytecount, { cec->getIndex(), cec->getValue() }); return (cachedConstants[c] = ret); @@ -331,11 +329,25 @@ namespace interp auto ptr = cas->getData(); auto len = cas->getLength(); - auto bytecount = getSizeOfType(ptr->getType()) + getSizeOfType(len->getType()); + auto bytecount = getSizeOfType(cas->getType()); auto ret = constructStructThingy(cas, bytecount, { ptr, len }); return (cachedConstants[c] = ret); } + else if(auto cds = dcast(fir::ConstantDynamicString, c)) + { + auto str = makeGlobalString(is, cds->getValue()); + auto ptr = fir::ConstantBitcast::get(fir::ConstantInt::getUNative(reinterpret_cast(str)), fir::Type::getInt8Ptr()); + auto len = fir::ConstantInt::getNative(cds->getValue().size()); + + auto bytecount = getSizeOfType(cds->getType()); + + // add -1 for capacity and 0 for refcountptr. + auto ret = constructStructThingy(cds, bytecount, { ptr, len, + fir::ConstantInt::getNative(-1), fir::ConstantInt::getNative(0) }); + + return (cachedConstants[c] = ret); + } else if(auto cda = dcast(fir::ConstantDynamicArray, c)) { std::vector mems; @@ -505,6 +517,13 @@ namespace interp } } #endif + + // we need to run the global constructor function! + if(auto gif = this->module->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION))) + { + auto cgif = this->compileFunction(gif); + this->runFunction(cgif, { }); + } } @@ -573,7 +592,20 @@ namespace interp + static size_t getOffsetOfTypeInTypeList(const std::vector& elms, size_t idx) + { + size_t ofs = 0; + for(uint64_t i = 0; i < idx; i++) + { + auto aln = getAlignmentOfType(elms[i]); + if(ofs % aln > 0) + ofs += (aln - (ofs % aln)); + + ofs += getSizeOfType(elms[i]); + } + return ofs; + } static std::vector getTypeListOfType(fir::Type* ty) { @@ -662,11 +694,9 @@ namespace interp else { auto typelist = getTypeListOfType(str.type); - iceAssert(idx < typelist.size()); - for(size_t i = 0; i < idx; i++) - ofs += getSizeOfType(typelist[i]); + ofs = getOffsetOfTypeInTypeList(typelist, idx); } uintptr_t dst = 0; @@ -706,12 +736,9 @@ namespace interp else { auto typelist = getTypeListOfType(str.type); - iceAssert(idx < typelist.size()); - for(size_t i = 0; i < idx; i++) - ofs += getSizeOfType(typelist[i]); - + ofs = getOffsetOfTypeInTypeList(typelist, idx); elm = typelist[idx]; } @@ -1198,7 +1225,7 @@ namespace interp } } - error("interp: invaild state"); + error("interp: invalid state"); } @@ -1292,10 +1319,7 @@ namespace interp std::vector elms = getTypeListOfType(strty); - size_t ofs = 0; - for(uint64_t i = 0; i < idx; i++) - ofs += getSizeOfType(elms[i]); - + size_t ofs = getOffsetOfTypeInTypeList(elms, idx); uintptr_t src = getActualValue(str); src += ofs; @@ -2534,6 +2558,28 @@ namespace interp #define gav getActualValue + auto extractOneValue = [this](const interp::Value& v, size_t idx, fir::Type* ty) -> interp::Value { + auto tmp = fir::ConstantValue::getZeroValue(ty); + auto x = doExtractValue(this, tmp, v, idx); + delete tmp; + + return x; + }; + + auto extractValueList = [this, extractOneValue](const interp::Value& v, const std::vector& tys) + -> std::vector + { + std::vector elms; + + for(size_t i = 0; i < tys.size(); i++) + { + auto x = extractOneValue(v, i, tys[i]); + elms.push_back(this->unwrapInterpValueIntoConstant(x)); + } + + return elms; + }; + if(ty->isPrimitiveType() || ty->isBoolType()) { if(ty == fir::Type::getBool()) return fir::ConstantBool::get(gav(val)); @@ -2548,6 +2594,50 @@ namespace interp else if(ty == fir::Type::getFloat32()) return fir::ConstantFP::get(ty, gav(val)); else if(ty == fir::Type::getFloat64()) return fir::ConstantFP::get(ty, gav(val)); } + else if(ty->isTupleType()) + { + return fir::ConstantTuple::get(extractValueList(val, ty->toTupleType()->getElements())); + } + else if(ty->isArrayType()) + { + return fir::ConstantArray::get(ty, extractValueList(val, + std::vector(ty->toArrayType()->getArraySize(), ty->getArrayElementType()))); + } + else if(ty->isCharSliceType()) + { + // do a bit of stuff -- extract the pointer, then the length. + char* ptr = gav(extractOneValue(val, SLICE_DATA_INDEX, ty->toArraySliceType()->getDataPointerType())); + int64_t len = gav(extractOneValue(val, SLICE_LENGTH_INDEX, fir::Type::getInt64())); + + return fir::ConstantCharSlice::get(std::string(ptr, len)); + } + else if(ty->isStringType()) + { + char* ptr = gav(extractOneValue(val, SAA_DATA_INDEX, fir::Type::getMutInt8Ptr())); + int64_t len = gav(extractOneValue(val, SAA_LENGTH_INDEX, fir::Type::getInt64())); + + return fir::ConstantDynamicString::get(std::string(ptr, len)); + } + else if(ty->isArraySliceType()) + { + auto ptr = this->unwrapInterpValueIntoConstant(extractOneValue(val, SLICE_DATA_INDEX, + ty->toArraySliceType()->getDataPointerType())); + + auto len = this->unwrapInterpValueIntoConstant(extractOneValue(val, SLICE_LENGTH_INDEX, + fir::Type::getInt64())); + + return fir::ConstantArraySlice::get(ty->toArraySliceType(), ptr, len); + } + else if(ty->isDynamicArrayType()) + { + auto ptr = this->unwrapInterpValueIntoConstant(extractOneValue(val, SAA_DATA_INDEX, + ty->getArrayElementType()->getMutablePointerTo())); + + auto len = this->unwrapInterpValueIntoConstant(extractOneValue(val, SAA_LENGTH_INDEX, fir::Type::getInt64())); + auto cap = this->unwrapInterpValueIntoConstant(extractOneValue(val, SAA_CAPACITY_INDEX, fir::Type::getInt64())); + + return fir::ConstantDynamicArray::get(ty->toDynamicArrayType(), ptr, len, cap); + } #undef gav diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index d1b8e56d..233a00ce 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -12,7 +12,7 @@ namespace parser using TT = lexer::TokenType; using namespace ast; - Stmt* parseIfStmt(State& st) + PResult parseIfStmt(State& st) { using Case = IfStmt::Case; @@ -61,7 +61,11 @@ namespace parser st.eat(); } - cases.back().body = parseBracedBlock(st).val(); + if(auto x = parseBracedBlock(st); x.isError()) + return PResult::copyError(x); + + else + cases.back().body = x.val(); } @@ -100,7 +104,13 @@ namespace parser st.eat(); } - c.body = parseBracedBlock(st).val(); + + if(auto x = parseBracedBlock(st); x.isError()) + return PResult::copyError(x); + + else + c.body = x.val(); + cases.push_back(c); } else if(st.frontAfterWS() == TT::LBrace || st.frontAfterWS() == TT::FatRightArrow) diff --git a/source/include/codegen.h b/source/include/codegen.h index afeea5de..d1fea629 100644 --- a/source/include/codegen.h +++ b/source/include/codegen.h @@ -81,7 +81,6 @@ namespace cgn std::vector methodSelfStack; fir::Function* globalInitFunc = 0; - std::vector> globalInits; util::hash_map methodList; @@ -150,8 +149,8 @@ namespace cgn fir::Function* getOrDeclareLibCFunction(std::string name); - void addGlobalInitialiser(fir::Value* storage, fir::Value* value); - + bool isInsideGlobalInitFunc = false; + bool isWithinGlobalInitFunction(); fir::IRBlock* enterGlobalInitFunction(); void leaveGlobalInitFunction(fir::IRBlock* restore); void finishGlobalInitFunction(); diff --git a/source/include/defs.h b/source/include/defs.h index 4db8295c..46f1e27d 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -425,6 +425,13 @@ struct OverloadError : ErrorMsg +struct ErrorException : public std::exception +{ + ErrorException(ErrorMsg* msg) : err(msg) { } + + ErrorMsg* err; +}; + struct TCResult { diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 979fc917..1f33a26a 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -226,6 +226,23 @@ namespace fir ConstantArray* arr = 0; }; + // this is the 'string' type!! + struct ConstantDynamicString : ConstantValue + { + friend struct Module; + + static ConstantDynamicString* get(const std::string& s); + std::string getValue(); + + virtual std::string str() override; + + protected: + ConstantDynamicString(const std::string& s); + + std::string value; + }; + + struct ConstantArraySlice : ConstantValue { friend struct Module; diff --git a/source/include/ir/type.h b/source/include/ir/type.h index 236a9735..ee759586 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -106,7 +106,7 @@ namespace fir virtual std::string str() = 0; virtual std::string encodedStr() = 0; virtual bool isTypeEqual(Type* other) = 0; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) = 0; + virtual Type* substitutePlaceholders(const util::hash_map& subst) = 0; Type* getPointerTo(); Type* getMutablePointerTo(); @@ -187,7 +187,7 @@ namespace fir bool isPolyPlaceholderType(); bool containsPlaceholders(); - std::vector getContainedPlaceholders(); + std::vector getContainedPlaceholders(); size_t getBitWidth(); @@ -307,7 +307,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~BoolType() override { } @@ -326,7 +326,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~VoidType() override { } @@ -345,7 +345,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~NullType() override { } @@ -370,7 +370,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; static ConstantNumberType* get(bool neg, bool flt, size_t bits); @@ -393,7 +393,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; std::string getName(); int getGroup(); @@ -426,7 +426,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; enum class Kind @@ -492,7 +492,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~PointerType() override { } @@ -532,7 +532,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~TupleType() override { } @@ -555,7 +555,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; Identifier getTypeName(); @@ -597,7 +597,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~UnionVariantType() override { } @@ -630,7 +630,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; virtual ~RawUnionType() override { } @@ -661,7 +661,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~TraitType() override { } @@ -701,7 +701,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~StructType() override { } @@ -786,7 +786,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~ClassType() override { } @@ -817,7 +817,7 @@ namespace fir util::hash_map reverseVirtualMethodMap; //* note: we do it this way (where we *EXCLUDE THE SELF POINTER*), because it's just easier -- to compare, and everything. - //* we really don't have a use for mapping a fir::Function to an index, only the other way. + //* we really don't have a use for mapping a Function to an index, only the other way. std::map>, size_t> virtualMethodMap; ClassType* baseClass = 0; @@ -844,16 +844,16 @@ namespace fir Type* getCaseType(); Identifier getTypeName(); - fir::ConstantValue* getNameArray(); - fir::ConstantValue* getCaseArray(); + ConstantValue* getNameArray(); + ConstantValue* getCaseArray(); - void setNameArray(fir::ConstantValue* arr); - void setCaseArray(fir::ConstantValue* arr); + void setNameArray(ConstantValue* arr); + void setCaseArray(ConstantValue* arr); virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~EnumType() override { } @@ -863,8 +863,8 @@ namespace fir Type* caseType; Identifier typeName; - fir::ConstantValue* runtimeNameArray = 0; - fir::ConstantValue* runtimeCasesArray = 0; + ConstantValue* runtimeNameArray = 0; + ConstantValue* runtimeCasesArray = 0; // static funcs public: @@ -888,7 +888,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~ArrayType() override { } @@ -915,7 +915,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~DynamicArrayType() override { } @@ -941,10 +941,13 @@ namespace fir bool isMutable(); bool isVariadicType(); + // simplifies the mutability checking and stuff. + Type* getDataPointerType(); + virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~ArraySliceType() override { } @@ -985,7 +988,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor virtual ~FunctionType() override { } @@ -1015,7 +1018,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor @@ -1035,7 +1038,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor @@ -1056,7 +1059,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; // protected constructor @@ -1077,7 +1080,7 @@ namespace fir virtual std::string str() override; virtual std::string encodedStr() override; virtual bool isTypeEqual(Type* other) override; - virtual fir::Type* substitutePlaceholders(const util::hash_map& subst) override; + virtual Type* substitutePlaceholders(const util::hash_map& subst) override; size_t getTypeSizeInBits() { return this->typeSizeInBits; } @@ -1101,13 +1104,13 @@ namespace fir struct LocatedType { LocatedType() { } - explicit LocatedType(fir::Type* t) : type(t) { } - LocatedType(fir::Type* t, const Location& l) : type(t), loc(l) { } + explicit LocatedType(Type* t) : type(t) { } + LocatedType(Type* t, const Location& l) : type(t), loc(l) { } - operator fir::Type* () const { return this->type; } - fir::Type* operator -> () const { return this->type; } + operator Type* () const { return this->type; } + Type* operator -> () const { return this->type; } - fir::Type* type = 0; + Type* type = 0; Location loc; }; diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index c439e2ac..acaa057a 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -356,7 +356,7 @@ namespace parser T* val() const { - if(this->state != 0) compiler_crash("no value"); + if(this->state != 0) this->error->postAndQuit(); else return this->value; } @@ -480,7 +480,7 @@ namespace parser ast::LitArray* parseArray(State& st, bool israw); ast::Stmt* parseForLoop(State& st); - ast::Stmt* parseIfStmt(State& st); + PResult parseIfStmt(State& st); PResult parseWhileLoop(State& st); ast::TopLevelBlock* parseTopLevel(State& st, const std::string& name); diff --git a/source/include/zpr.h b/source/include/zpr.h index a10e43e5..53c46b6a 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -274,12 +274,38 @@ namespace zpr return fmt; } + inline int print(const std::string& fmt) + { + return printf("%s", fmt.c_str()); + } + + inline int println(const std::string& fmt) + { + return printf("%s\n", fmt.c_str()); + } + + + template std::string sprint(const std::string& fmt, Args&&... xs) { return _internal::sprint(fmt.c_str(), xs...); } + template + int print(const std::string& fmt, Args&&... xs) + { + auto x = _internal::sprint(fmt.c_str(), xs...); + return printf("%s", x.c_str()); + } + + template + int println(const std::string& fmt, Args&&... xs) + { + auto x = _internal::sprint(fmt.c_str(), xs...); + return printf("%s\n", x.c_str()); + } + diff --git a/source/include/ztmu.h b/source/include/ztmu.h index bb2d45b7..20368b0d 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -163,7 +163,7 @@ namespace detail -#if ZTMU_CREATE_IMPL +#if ZTMU_CREATE_IMPL || true // comes as a pair yo #include "zpr.h" @@ -214,11 +214,12 @@ namespace detail #endif - #if 0 + #if 1 template void ztmu_dbg(const char* fmt, Args&&... args) { - fprintf(stderr, "%s", zpr::sprint(fmt, args...).c_str()); + if(!isatty(STDERR_FILENO)) + fprintf(stderr, "%s", zpr::sprint(fmt, args...).c_str()); } #else template @@ -439,6 +440,9 @@ namespace detail { if(isInRawMode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios) != -1) isInRawMode = false; + + // disable bracketed paste: + platform_write("\x1b[?2004l"); } static void enterRawMode() @@ -471,7 +475,7 @@ namespace detail // control modes - set 8 bit chars raw.c_cflag |= (CS8); - // local modes - choing off, canonical off, no extended functions, + // local modes - echoing off, canonical off, no extended functions, // no signal chars (^Z,^C) raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); @@ -484,6 +488,8 @@ namespace detail if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) return; + // enable bracketed paste: + platform_write("\x1b[?2004h"); isInRawMode = true; } @@ -646,6 +652,39 @@ namespace detail return hcursor; } + static size_t calculate_wli(State* st, size_t len) + { + size_t done = 0; + size_t remaining = len; + + size_t lc = 1; + size_t wli = 0; + + while(true) + { + auto left = st->termWidth - (lc == 1 ? (st->lineIdx == 0 ? st->normPL : st->contPL) : st->wrapPL); + auto todo = std::min(left, remaining); + + if(remaining == left) + wli++; + + remaining -= todo; + done += todo; + + if(remaining == 0) + break; + + // if your cursor is beyond this point, then you are on the next line. + if(st->cursor >= done + 1) + { + wli++; + lc++; + } + } + + return wli; + } + // returns (linesBelowCursor, finalHcursor) @@ -669,7 +708,7 @@ namespace detail size_t lines = 1; while(len > 0) { - auto left = width - (lines == 1 ? st->normPL : st->wrapPL); + auto left = width - (lines == 1 ? (st->lineIdx == 0 ? st->normPL : st->contPL) : st->wrapPL); if(left == len) lines += 1; @@ -683,38 +722,6 @@ namespace detail return lines; }; - auto calc_wli = [&](size_t len) -> size_t { - size_t done = 0; - size_t remaining = len; - - size_t lc = 1; - size_t wli = 0; - - while(true) - { - auto left = st->termWidth - (lc == 1 ? st->normPL : st->wrapPL); - auto todo = std::min(left, remaining); - - if(remaining == left) - wli++; - - remaining -= todo; - done += todo; - - if(remaining == 0) - break; - - // if your cursor is beyond this point, then you are on the next line. - if(st->cursor >= done + 1) - { - wli++; - lc++; - } - } - - return wli; - }; - auto getPromptForLine = [&](size_t wli) -> std::pair { if(lineIdx == 0) { @@ -741,7 +748,7 @@ namespace detail size_t old_NWL = calc_wrap(displayedTextLength(oldLine)); - auto new_wli = calc_wli(st->cursor); + auto new_wli = calculate_wli(st, st->cursor); qcmd(moveCursorLeft(9999)); @@ -902,7 +909,7 @@ namespace detail ztmu_dbg("hcursor = %zu\n", hcursor); - st->wrappedLineIdx = calc_wli(st->cursor); + st->wrappedLineIdx = calculate_wli(st, st->cursor); ztmu_dbg("updated wli: %zu\n\n", st->wrappedLineIdx); if(defer_flush) *commandBuffer = commands; @@ -1108,6 +1115,7 @@ namespace detail prev_right_margin, std::string(prevLine).c_str()); st->lineIdx -= 1; + st->wrappedLineIdx = calculate_wli(st, st->cursor); // and then we move the cursor. platform_write(moveCursorUp(1)); @@ -1244,7 +1252,7 @@ namespace detail if(platform_read_one(&c) <= 0) break; - // ztmu_dbg("[%d]", c); + ztmu_dbg("[%d]", static_cast(c)); switch(c) { // enter @@ -1319,7 +1327,6 @@ namespace detail case ESC: { // there should be at least two more! char seq[2]; platform_read(seq, 2); - char thing = 0; if(seq[0] == '[') { @@ -1354,6 +1361,43 @@ namespace detail { thing = n; } + + if(n == 200) + { + ztmu_dbg("PASTE!\n"); + + // keep reading until we find an esc. + std::string pasted; + + char x = 0; + while(platform_read_one(&x) > 0) + { + pasted += x; + if(pasted.rfind("\x1b[201~") != -1) + break; + } + + // get rid of the thing at the end. + pasted.erase(pasted.rfind("\x1b[201~")); + + ztmu_dbg("PASTED: '%s'\n", pasted); + + auto oldline = getCurLine(st); + + auto len = displayedTextLength(pasted); + + getCurLine(st).insert(st->byteCursor, pasted); + + st->cursor += len; + st->byteCursor += pasted.size(); + + ztmu_dbg("c: %zu, bc: %zu\n", st->cursor, st->byteCursor); + + + refresh_line(st, &oldline); + + goto loop_top; + } } else { diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index fef5f1f3..4c9620b3 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -12,7 +12,7 @@ sst::Stmt* TCResult::stmt() const { if(this->_kind == RK::Error) - this->_pe->postAndQuit(); + throw ErrorException(this->_pe); //this->_pe->postAndQuit(); switch(this->_kind) { @@ -26,7 +26,7 @@ sst::Stmt* TCResult::stmt() const sst::Expr* TCResult::expr() const { if(this->_kind == RK::Error) - this->_pe->postAndQuit(); + throw ErrorException(this->_pe); // this->_pe->postAndQuit(); if(this->_kind != RK::Expression) _error_and_exit("not expr\n"); @@ -37,7 +37,7 @@ sst::Expr* TCResult::expr() const sst::Defn* TCResult::defn() const { if(this->_kind == RK::Error) - this->_pe->postAndQuit(); + throw ErrorException(this->_pe); //this->_pe->postAndQuit(); if(this->_kind != RK::Definition) _error_and_exit("not defn\n"); diff --git a/source/platform/backtrace.cpp b/source/platform/backtrace.cpp index e0e7597f..83867361 100644 --- a/source/platform/backtrace.cpp +++ b/source/platform/backtrace.cpp @@ -103,7 +103,7 @@ namespace platform piece.demangledName = piece.mangledName; } - debuglogln(" %2d: %12p | %s + %#x", piece.num, piece.address, piece.demangledName, piece.offset); + debuglogln(" %2d: %12x | %s + %#x", piece.num, piece.address, piece.demangledName, piece.offset); } else { diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 8fc9656c..4b706821 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -15,20 +15,22 @@ namespace repl static void runCommand(const std::string& s) { if(s == "q") exit(0); + else if(s == "reset") { zpr::println("resetting environment..."); setupEnvironment(); } else if(s == "help") repl::error("no help implemented. ggwp."); else repl::error("invalid command '%s'.", s); } - static constexpr const char* PROMPT_STRING = COLOUR_BLUE " * " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; - static constexpr const char* WRAP_PROMPT_STRING = COLOUR_GREY_BOLD " |" COLOUR_RESET " "; - static constexpr const char* CONTINUATION_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* PROMPT_STRING = COLOUR_BLUE " * " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* WRAP_PROMPT_STRING = COLOUR_GREY_BOLD " |" COLOUR_RESET " "; + static constexpr const char* CONT_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; - static constexpr const char* EXTRA_INDENT = " "; + static constexpr const char* EXTRA_INDENT = " "; + static constexpr size_t EXTRA_INDENT_LEN = std::char_traits::length(EXTRA_INDENT); void start() { - printf("flax repl -- version %s\n", frontend::getVersion().c_str()); - printf("type :help for help\n\n"); + zpr::println("flax repl -- version %s", frontend::getVersion()); + zpr::println("type :help for help\n"); setupEnvironment(); @@ -36,14 +38,20 @@ namespace repl auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); + st.setContPrompt(CONT_PROMPT_STRING); st.setWrappedPrompt(WRAP_PROMPT_STRING); - st.setContPrompt(CONTINUATION_PROMPT_STRING); - st.setKeyHandler(static_cast('}'), [](ztmu::State* st, ztmu::Key k) -> ztmu::HandlerAction { + // we need to put this up here, so the handler can capture it. + int indentLevel = 0; + + st.setKeyHandler(static_cast('}'), [&indentLevel](ztmu::State* st, ztmu::Key k) -> ztmu::HandlerAction { // a bit dirty, but we just do this -- if we can find the indent at the back, then remove it. auto line = st->getCurrentLine(); - if(line.size() >= 2 && line.find(EXTRA_INDENT, line.size() - strlen(EXTRA_INDENT)) != -1) + if(indentLevel > 0 && line.size() >= 2 && line.find(EXTRA_INDENT, line.size() - EXTRA_INDENT_LEN) != -1) + { st->setCurrentLine(line.substr(0, line.size() - 2)); + indentLevel--; + } return ztmu::HandlerAction::CONTINUE; }); @@ -62,20 +70,27 @@ namespace repl } else if(bool needmore = processLine(input); needmore) { - const char* indent = ""; - switch(input.back()) - { - case '{': [[fallthrough]]; - case '(': [[fallthrough]]; - case '[': [[fallthrough]]; - case ',': - indent = EXTRA_INDENT; - } + auto calc_indent = [](char c) -> int { + switch(c) + { + case '{': [[fallthrough]]; + case '(': [[fallthrough]]; + case '[': [[fallthrough]]; + case ',': + return 1; + } + + return 0; + }; + + indentLevel = calc_indent(input.back()); // read more. - while(auto line = st.readContinuation(indent)) + while(auto line = st.readContinuation(std::string(indentLevel * EXTRA_INDENT_LEN, ' '))) { input += "\n" + std::string(*line); + indentLevel += calc_indent(input.back()); + needmore = processLine(input); if(!needmore) diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index fe4d935b..054825de 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -11,12 +11,14 @@ #include "typecheck.h" #include "ir/module.h" +#include "ir/interp.h" #include "ir/irbuilder.h" #include "memorypool.h" // defined in codegen/directives.cpp -fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, const Identifier& fname); +fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, + const Identifier& fname, fir::interp::InterpState* is = 0); namespace repl { @@ -35,14 +37,24 @@ namespace repl this->fs = new sst::TypecheckState(tree); this->cs = new cgn::CodegenState(fir::IRBuilder(this->module)); this->cs->module = this->module; + this->interpState = new fir::interp::InterpState(this->module); // so we don't crash, give us a starting location. this->cs->pushLoc(Location()); } + ~State() + { + delete this->interpState; + delete this->cs; + delete this->fs; + delete this->module; + } + fir::Module* module; - sst::TypecheckState* fs; cgn::CodegenState* cs; + sst::TypecheckState* fs; + fir::interp::InterpState* interpState; size_t fnCounter = 0; size_t varCounter = 0; @@ -51,6 +63,9 @@ namespace repl static State* state = 0; void setupEnvironment() { + if(state) + delete state; + state = new State(); } @@ -82,11 +97,29 @@ namespace repl // it will store the relevant state into the TypecheckState. { auto stmt = _stmt.val(); - auto tcr = stmt->typecheck(state->fs); + + // ugh. + auto tcr = TCResult(reinterpret_cast(0)); + + try + { + tcr = stmt->typecheck(state->fs); + } + catch(ErrorException& ee) + { + ee.err->post(); + printf("\n"); + + return false; + } + + if(tcr.isError()) { tcr.error()->post(); + printf("\n"); + return false; } else if(!tcr.isParametric() && !tcr.isDummy()) diff --git a/source/typecheck/directives.cpp b/source/typecheck/directives.cpp index d4e8c960..f8833506 100644 --- a/source/typecheck/directives.cpp +++ b/source/typecheck/directives.cpp @@ -42,7 +42,8 @@ TCResult ast::RunDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) // defined in codegen/directives.cpp -fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, const Identifier& fname); +fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, + const Identifier& fname, fir::interp::InterpState* is = 0); static size_t condCounter = 0; TCResult ast::IfDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 1285ab78..1ebbb6f6 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -374,12 +374,20 @@ namespace sst if(addPreludeDefinitions) generatePreludeDefinitions(fs); - auto tns = dcast(NamespaceDefn, file.root->typecheck(fs).stmt()); - iceAssert(tns); + // handle exception here: + try { + auto tns = dcast(NamespaceDefn, file.root->typecheck(fs).stmt()); + iceAssert(tns); - tns->name = file.moduleName; + tns->name = file.moduleName; + + fs->dtree->topLevel = tns; + } + catch (ErrorException& ee) + { + ee.err->postAndQuit(); + } - fs->dtree->topLevel = tns; return fs->dtree; } } diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 75e50e3a..430c33fe 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -148,7 +148,9 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) defer(fs->popLoc()); if(this->name == "_") - error(this, "'_' is a discarding binding; it does not yield a value and cannot be referred to"); + return TCResult( + SimpleError::make(this->loc, "'_' is a discarding binding; it does not yield a value and cannot be referred to") + ); // else if(this->name == "::" || this->name == "^") // error(this, "invalid use of scope-path-specifier '%s' in a non-scope-path context", this->name); @@ -285,7 +287,9 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) } // ok, we haven't found anything - error(this, "reference to unknown entity '%s'", this->name); + return TCResult( + SimpleError::make(this->loc, "reference to unknown entity '%s'", this->name) + ); } From 7bd8dd879ea8d64a64d18221e3550c5651a48c62 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 13 Nov 2019 12:19:31 +0800 Subject: [PATCH 069/129] history works --- source/include/ztmu.h | 212 +++++++++++++++++++++++++++++++++------- source/repl/driver.cpp | 2 + source/repl/execute.cpp | 2 + 3 files changed, 180 insertions(+), 36 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 20368b0d..d9a39894 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -58,6 +58,11 @@ namespace ztmu void setContPrompt(const std::string& prompt); void setWrappedPrompt(const std::string& prompt); + void useUniqueHistory(bool enable); + void addPreviousInputToHistory(); + void loadHistory(const std::vector>& h); + std::vector> getHistory(); + void move_cursor_left(int n); void move_cursor_right(int n); @@ -97,6 +102,11 @@ namespace ztmu std::vector lines; std::map> keyHandlers; + + std::vector> history; + std::vector savedCurrentInput; + bool uniqueHistory = true; + size_t historyIdx = 0; }; } @@ -685,6 +695,26 @@ namespace detail return wli; } + static size_t calculate_nwl(State* st, size_t lineIdx, size_t len) + { + auto width = st->termWidth; + + size_t lines = 1; + while(len > 0) + { + auto left = width - (lines == 1 ? (lineIdx == 0 ? st->normPL : st->contPL) : st->wrapPL); + if(left == len) + lines += 1; + + len -= std::min(left, len); + if(len == 0) + break; + + lines += 1; + } + + return lines; + } // returns (linesBelowCursor, finalHcursor) @@ -702,26 +732,6 @@ namespace detail }; - auto calc_wrap = [&](size_t len) -> size_t { - auto width = st->termWidth; - - size_t lines = 1; - while(len > 0) - { - auto left = width - (lines == 1 ? (st->lineIdx == 0 ? st->normPL : st->contPL) : st->wrapPL); - if(left == len) - lines += 1; - - len -= std::min(left, len); - if(len == 0) - break; - - lines += 1; - } - - return lines; - }; - auto getPromptForLine = [&](size_t wli) -> std::pair { if(lineIdx == 0) { @@ -738,15 +748,14 @@ namespace detail std::string_view currentLine = st->lines[lineIdx]; - size_t numWrappingLines = calc_wrap(displayedTextLength(currentLine)); + size_t numWrappingLines = calculate_nwl(st, st->lineIdx, displayedTextLength(currentLine)); // if we're operating on the current line, shun bian cache the NWL count. // we'll use this for cursor_down among other things. if(st->lineIdx == lineIdx) st->cachedNWLForCurrentLine = numWrappingLines; - - size_t old_NWL = calc_wrap(displayedTextLength(oldLine)); + size_t old_NWL = calculate_nwl(st, st->lineIdx, displayedTextLength(oldLine)); auto new_wli = calculate_wli(st, st->cursor); @@ -923,7 +932,7 @@ namespace detail { // refresh the top line manually: - ztmu_dbg("\n\n** refreshing line 0...\n"); + ztmu_dbg("\n\n** refreshing line %d...\n", st->lineIdx); auto [ down, hcursor ] = _refresh_line(st, st->lineIdx, /* skip_cursor: */ false, oldLine ? *oldLine : st->lines[st->lineIdx], /* defer_flush: */ false, /* cmd_buffer: */ nullptr); @@ -946,6 +955,11 @@ namespace detail st->byteCursor = 0; st->cursor = 0; + // see if we need to scroll. note that _refresh_line will scroll for the current line -- ONLY IF IT WRAPS + // but because we are drawing all lines, we need space for the next line as well. + if(getCursorPosition().y == st->termHeight) + buffer += zpr::sprint("%s1S", CSI); // one should be enough. + buffer += moveCursorDown(down); downAccum += down; @@ -953,7 +967,7 @@ namespace detail std::string buf; down = _refresh_line(st, i, /* skip_cursor: */ true, st->lines[i], /* defer_flush: */ true, - &buf).first - 1; + &buf).first + 1; buffer += buf; @@ -1057,6 +1071,45 @@ namespace detail } } + static size_t _calculate_total_nwl_for_all_lines(State* st) + { + size_t down = 0; + for(size_t i = 0; i < st->lines.size(); i++) + down += calculate_nwl(st, i, displayedTextLength(st->lines[i])); + + return down; + } + + // shared stuff that history-manipulating people need to do, basically moving the + // cursor all the way to the end of the text, including the physical cursor. + static void _move_cursor_to_end_of_input(State* st) + { + // we want the cursor to be at the very end of the last line of input! + + // but first, go to the very beginning, and refresh -- this draws all the lines. + st->lineIdx = 0; + st->cursor = 0; + st->byteCursor = 0; + st->wrappedLineIdx = 0; + + refresh_line(st); + + // then we can move to the bottom and refresh again. + { + size_t down = _calculate_total_nwl_for_all_lines(st) - 1; + + platform_write(moveCursorDown(down)); + } + + st->cursor = displayedTextLength(st->lines.back()); + st->byteCursor = convertCursorToByteCursor(st->lines.back().c_str(), st->cursor); + + st->lineIdx = st->lines.size() - 1; + st->wrappedLineIdx = calculate_wli(st, st->cursor); + + refresh_line(st); + } + static void cursor_up(State* st) { if(st->cursor > 0 && st->wrappedLineIdx > 0) @@ -1125,9 +1178,29 @@ namespace detail ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, prev_right_margin, std::string(prevLine).c_str()); } - else + else if(st->history.size() > 0 && st->historyIdx < st->history.size()) { - // TODO: possibly handle history? + ztmu_dbg("## history up (%zu)\n", st->historyIdx); + + // save it, so we can go back to it. + if(st->historyIdx == 0) + st->savedCurrentInput = st->lines; + + // ok, so we're already at the top of the thing, so we should clear the screen from here + // to the bottom. + platform_write(zpr::sprint("%s0J", CSI)); + + // set the index, set the lines, and refresh. + st->historyIdx += 1; + st->lines = st->history[st->history.size() - st->historyIdx]; + + ztmu_dbg("setting: \n"); + for(const auto& l : st->lines) + ztmu_dbg("'%s'\n", l); + + ztmu_dbg("\n"); + + _move_cursor_to_end_of_input(st); } } @@ -1161,8 +1234,6 @@ namespace detail auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, st->lineIdx == 0 ? st->normPL: st->contPL, st->wrapPL); - // auto leftChars = hcursor - (st->wrappedLineIdx > 0 ? st->wrapPL : (st->lineIdx == 0 ? st->normPL : st->contPL)); - // ok, the next line will definitely be a continuation prompt. so, see how many chars into the line // we'll actually put ourselves -- and handle the edge case of negative values! auto next_leftChars = (hcursor < st->contPL @@ -1179,10 +1250,32 @@ namespace detail platform_write(moveCursorDown(1)); refresh_line(st); } - else + else if(st->historyIdx > 0) { - // TODO: handle moving down into the next "line" list -- during continuations. - // TODO: possibly handle history? + ztmu_dbg("## history down (%zu)\n", st->historyIdx); + + // somewhat similar to moving up in history. first, we will already be at the bottom of + // all lines. so, we need to move the physical and virtual cursor to the top of the entire + // input. to do this, calculate the total NWL of all lines: + { + // we must do this for the current input state, before we change into the history. + auto up = _calculate_total_nwl_for_all_lines(st) - 1; + + // as we move up, we must clear the entire line, to get rid of the lingering text. + for(size_t i = 0; i < up; i++) + { + platform_write(zpr::sprint("%s2K", CSI)); + platform_write(moveCursorUp(1)); + } + } + + // ok, now we can change: + st->historyIdx -= 1; + if(st->historyIdx == 0) st->lines = st->savedCurrentInput, st->savedCurrentInput.clear(); + else st->lines = st->history[st->history.size() - st->historyIdx]; + + // now, fix it. + _move_cursor_to_end_of_input(st); } } @@ -1239,9 +1332,6 @@ namespace detail didSetSignalHandler = true; - - - bool eof = false; while(true) { @@ -1507,7 +1597,18 @@ namespace detail // move the cursor to the end, refresh the line, then leave raw mode -- this makes sure // that we leave the cursor in a nice place after the call to this function returns. - cursor_end(st); + { + cursor_end(st); + + // see how many lines we need to go down. + size_t down = 0; + for(size_t i = st->lineIdx + 1; i < st->lines.size(); i++) + down += calculate_nwl(st, i, displayedTextLength(st->lines[i])); + + platform_write(moveCursorDown(down)); + platform_write(moveCursorLeft(9999)); + } + leaveRawMode(); platform_write("\n"); @@ -1526,8 +1627,12 @@ namespace ztmu void State::clear() { // reset the thing. + this->savedCurrentInput.clear(); this->lines.clear(); this->lineIdx = 0; + + this->cachedNWLForCurrentLine = 0; + this->wrappedLineIdx = 0; } void State::setPrompt(const std::string& prompt) @@ -1632,6 +1737,41 @@ namespace ztmu detail::refresh_line(this, &old); } + void State::useUniqueHistory(bool enable) + { + this->uniqueHistory = enable; + } + + // your responsibility to check if the input was empty! + // well if you unique it then you'll only get one empty history entry, but still. + void State::addPreviousInputToHistory() + { + // now, if we need to unique the history, then erase (if any) the + // prior occurrence of that item. we shouldn't really need to check for more + // than one, because any previous call to addHistory() should have already + // left us with at most two. + + // obviously, we check for dupes before adding the current entry, because then we'll + // just end up erasing the new entry like an idiot. + if(auto it = std::find(this->history.begin(), this->history.end(), this->lines); it != this->history.end()) + this->history.erase(it); + + // this must be called before the next invocation of read() or readContinuation() + // so that State->lines is still preserved. + this->history.push_back(this->lines); + } + + void State::loadHistory(const std::vector>& h) + { + this->history = h; + } + + // it's up to you to serialise it! + std::vector> State::getHistory() + { + return this->history; + } + std::optional State::read() { // clear. diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 4b706821..6a6978f4 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -98,6 +98,8 @@ namespace repl } } + st.addPreviousInputToHistory(); + // ok, we're done -- clear. input.clear(); } diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index 054825de..a1e83fab 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -71,6 +71,8 @@ namespace repl bool processLine(const std::string& line) { + fprintf(stderr, "line: '%s'\n", line.c_str()); + std::string replName = ""; frontend::CollectorState collector; From f9d22086d57ffddc88552d37c13eb44da6e6d002 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 13 Nov 2019 12:45:16 +0800 Subject: [PATCH 070/129] fix multiline --- source/include/ztmu.h | 132 ++++++++++++++++++++++++++++++----------- source/repl/driver.cpp | 20 ++++--- 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index d9a39894..20daf838 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -52,7 +52,7 @@ namespace ztmu void clear(); std::optional read(); - std::optional readContinuation(const std::string& seed = ""); + std::optional> readContinuation(const std::string& seed = ""); void setPrompt(const std::string& prompt); void setContPrompt(const std::string& prompt); @@ -1003,7 +1003,44 @@ namespace detail refresh_line(st); } - static void cursor_left(State* st, int n, bool refresh = true) + static void delete_left(State* st) + { + if(st->cursor > 0 && getCurLine(st).size() > 0) + { + auto x = st->byteCursor - 1; + auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); + + auto old = getCurLine(st); + + getCurLine(st).erase(l, x - l + 1); + st->byteCursor -= (x - l + 1); + st->cursor -= 1; + + refresh_line(st, &old); + } + } + + static void delete_right(State* st) + { + if(st->byteCursor < getCurLine(st).size()) + { + auto x = st->byteCursor; + auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); + + auto old = getCurLine(st); + + getCurLine(st).erase(x, l - x + 1); + + // both cursors remain unchanged. + refresh_line(st, &old); + } + } + + // we differentiate between _cursor_left() and cursor_left() -- the latter only moves along + // a single line ("logical line"), while the latter will move to the previous line if the current + // line is exhausted. user-inputs call cursor_left(), while internal users should call _cursor_left(). + + static void _cursor_left(State* st, int n, bool refresh = true) { for(int i = 0; i < n; i++) { @@ -1020,7 +1057,31 @@ namespace detail refresh_line(st); } - static void cursor_right(State* st, int n, bool refresh = true) + static void cursor_left(State* st, bool refresh = true) + { + if(st->cursor > 0) + { + _cursor_left(st, 1, refresh); + } + else if(st->lineIdx > 0) + { + st->lineIdx -= 1; + + // physical cursor needs to move up one. + platform_write(moveCursorUp(1)); + + st->cursor = displayedTextLength(st->lines[st->lineIdx]); + st->byteCursor = st->lines[st->lineIdx].size(); + st->wrappedLineIdx = calculate_wli(st, st->cursor); + + // need to refresh. + refresh_line(st); + } + } + + + // same deal with _cursor_right as for _cursor_left. + static void _cursor_right(State* st, int n, bool refresh = true) { for(int i = 0; i < n; i++) { @@ -1038,39 +1099,30 @@ namespace detail refresh_line(st); } - static void delete_left(State* st) + static void cursor_right(State* st, bool refresh = true) { - if(st->cursor > 0 && getCurLine(st).size() > 0) + if(st->byteCursor < getCurLine(st).size()) { - auto x = st->byteCursor - 1; - auto l = findBeginningOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); - - auto old = getCurLine(st); - - getCurLine(st).erase(l, x - l + 1); - st->byteCursor -= (x - l + 1); - st->cursor -= 1; - - refresh_line(st, &old); + _cursor_right(st, 1, refresh); } - } - - static void delete_right(State* st) - { - if(st->byteCursor < getCurLine(st).size()) + else if(st->lineIdx + 1 < st->lines.size()) { - auto x = st->byteCursor; - auto l = findEndOfUTF8CP(reinterpret_cast(getCurLine(st).c_str()), x); + st->lineIdx += 1; - auto old = getCurLine(st); + // physical cursor needs to move down one. + platform_write(moveCursorDown(1)); - getCurLine(st).erase(x, l - x + 1); + st->cursor = 0; + st->byteCursor = 0; + st->wrappedLineIdx = 0; - // both cursors remain unchanged. - refresh_line(st, &old); + // need to refresh. + refresh_line(st); } } + + static size_t _calculate_total_nwl_for_all_lines(State* st) { size_t down = 0; @@ -1127,7 +1179,7 @@ namespace detail auto rightmargin = st->termWidth - hcursor; // this will stop when we can't go further, so there's no need to limit. - cursor_left(st, h_curlength + rightmargin); + _cursor_left(st, h_curlength + rightmargin); /* so what we want to do, ideally, is move "left" termWidth number of codepoints. HOWEVER, @@ -1172,7 +1224,7 @@ namespace detail // and then we move the cursor. platform_write(moveCursorUp(1)); - cursor_left(st, st->termWidth - hcursor - prev_right_margin, /* refresh: */ false); + _cursor_left(st, st->termWidth - hcursor - prev_right_margin, /* refresh: */ false); refresh_line(st); ztmu_dbg("c: %zu, bc: %zu, margin: %zu, pl: '%s'\n", st->cursor, st->byteCursor, @@ -1221,7 +1273,7 @@ namespace detail // in the next line, the prompt will always be a wrapping prompt. auto leftmargin = hcursor - st->wrapPL; - cursor_right(st, rightmargin + leftmargin); + _cursor_right(st, rightmargin + leftmargin); } else if(st->lineIdx + 1 < st->lines.size()) { @@ -1506,8 +1558,8 @@ namespace detail case 'A': cursor_up(st); break; case 'B': cursor_down(st); break; - case 'C': cursor_right(st, 1); break; - case 'D': cursor_left(st, 1); break; + case 'C': cursor_right(st); break; + case 'D': cursor_left(st); break; case 'H': cursor_home(st); break; case 'F': cursor_end(st); break; } @@ -1657,14 +1709,20 @@ namespace ztmu { if(n < 0) move_cursor_right(-n); - detail::cursor_left(this, n); + for(int i = 0; i < n; i++) + detail::cursor_left(this, /* refresh: */ false); + + detail::refresh_line(this); } void State::move_cursor_right(int n) { if(n < 0) move_cursor_left(-n); - detail::cursor_right(this, n); + for(int i = 0; i < n; i++) + detail::cursor_right(this, /* refresh: */ false); + + detail::refresh_line(this); } void State::move_cursor_up(int n) @@ -1673,6 +1731,8 @@ namespace ztmu for(int i = 0; i < n; i++) detail::cursor_up(this); + + detail::refresh_line(this); } void State::move_cursor_down(int n) @@ -1681,6 +1741,8 @@ namespace ztmu for(int i = 0; i < n; i++) detail::cursor_down(this); + + detail::refresh_line(this); } @@ -1782,7 +1844,7 @@ namespace ztmu else return this->lines.back(); } - std::optional State::readContinuation(const std::string& seed) + std::optional> State::readContinuation(const std::string& seed) { // don't clear this->lineIdx++; @@ -1790,7 +1852,7 @@ namespace ztmu bool eof = detail::read_line(this, /* promptMode: */ 1, seed); if(eof) return std::nullopt; - else return this->lines.back(); + else return this->lines; } } diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 6a6978f4..59bc84ed 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -34,8 +34,6 @@ namespace repl setupEnvironment(); - std::string input; - auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); st.setContPrompt(CONT_PROMPT_STRING); @@ -58,7 +56,7 @@ namespace repl while(auto line = st.read()) { - input += std::string(*line); + auto input = std::string(*line); if(input.empty()) continue; @@ -83,12 +81,20 @@ namespace repl return 0; }; + auto join_lines = [](const std::vector& lines) -> std::string { + std::string ret; + for(const auto& l : lines) + ret += "\n" + l; + + return ret; + }; + indentLevel = calc_indent(input.back()); // read more. - while(auto line = st.readContinuation(std::string(indentLevel * EXTRA_INDENT_LEN, ' '))) + while(auto lines = st.readContinuation(std::string(indentLevel * EXTRA_INDENT_LEN, ' '))) { - input += "\n" + std::string(*line); + auto input = join_lines(*lines); indentLevel += calc_indent(input.back()); needmore = processLine(input); @@ -100,8 +106,8 @@ namespace repl st.addPreviousInputToHistory(); - // ok, we're done -- clear. - input.clear(); + // add an extra line + printf("\n"); } } } From 00261fef49bcc94257ab57142c7fa486a6045976 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 13 Nov 2019 13:10:53 +0800 Subject: [PATCH 071/129] delete left/right now works across lines --- source/include/ztmu.h | 71 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 20daf838..ae950263 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -987,6 +987,16 @@ namespace detail + static size_t _calculate_total_nwl_for_all_lines(State* st, size_t startingLineIdx = 0) + { + size_t down = 0; + for(size_t i = startingLineIdx; i < st->lines.size(); i++) + down += calculate_nwl(st, i, displayedTextLength(st->lines[i])); + + return down; + } + + static void cursor_home(State* st) { st->cursor = 0; @@ -1003,6 +1013,9 @@ namespace detail refresh_line(st); } + + // note: internal code doesn't use delete_left or delete_right, so we don't need two + // versions that handle lines. static void delete_left(State* st) { if(st->cursor > 0 && getCurLine(st).size() > 0) @@ -1018,6 +1031,35 @@ namespace detail refresh_line(st, &old); } + else if(st->lineIdx > 0) + { + st->lineIdx -= 1; + + st->cursor = displayedTextLength(st->lines[st->lineIdx]); + st->byteCursor = st->lines[st->lineIdx].size(); + st->wrappedLineIdx = calculate_wli(st, st->cursor); + + // here's the thing: the remaining pieces of this line needs to be appended + // to the previous line! (+1 here cos we already -= 1 above) + auto remaining = st->lines[st->lineIdx + 1]; + st->lines[st->lineIdx].insert(st->byteCursor, remaining); + + // before we refresh, we need to go all the way to the bottom and erase the last physical line, + // so that it won't be lingering later on. + { + auto down = _calculate_total_nwl_for_all_lines(st, st->lineIdx) - 2; + ztmu_dbg("** down = %zu\n", down); + + // physical cursor needs to move up one, so move up one more than we moved down. + platform_write(zpr::sprint("%s%s2K%s", moveCursorDown(down), CSI, moveCursorUp(down + 1))); + } + + // then we need to erase this line from the lines! + st->lines.erase(st->lines.begin() + st->lineIdx + 1); + + // need to refresh. + refresh_line(st); + } } static void delete_right(State* st) @@ -1034,6 +1076,25 @@ namespace detail // both cursors remain unchanged. refresh_line(st, &old); } + else if(st->lineIdx + 1 < st->lines.size()) + { + // we don't move anything. all the text from the next line gets appended to the current line. + auto nextLine = st->lines[st->lineIdx + 1]; + st->lines[st->lineIdx].insert(st->byteCursor, nextLine); + + // before we refresh, we need to go all the way to the bottom and erase the last physical line, + // so that it won't be lingering later on. + { + auto down = _calculate_total_nwl_for_all_lines(st, st->lineIdx) - 1; + platform_write(zpr::sprint("%s%s2K%s", moveCursorDown(down), CSI, moveCursorUp(down))); + } + + // then we need to erase the next line from the lines! + st->lines.erase(st->lines.begin() + st->lineIdx + 1); + + // need to refresh. + refresh_line(st); + } } // we differentiate between _cursor_left() and cursor_left() -- the latter only moves along @@ -1122,16 +1183,6 @@ namespace detail } - - static size_t _calculate_total_nwl_for_all_lines(State* st) - { - size_t down = 0; - for(size_t i = 0; i < st->lines.size(); i++) - down += calculate_nwl(st, i, displayedTextLength(st->lines[i])); - - return down; - } - // shared stuff that history-manipulating people need to do, basically moving the // cursor all the way to the end of the text, including the physical cursor. static void _move_cursor_to_end_of_input(State* st) From 0c6f9b0746cd039bed99a4f4cfc4150b319e9205 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 13 Nov 2019 13:58:58 +0800 Subject: [PATCH 072/129] delete lines, general fixes --- source/include/ztmu.h | 107 +++++++++++++++++++++++++++++----------- source/repl/driver.cpp | 1 + source/repl/execute.cpp | 2 - 3 files changed, 80 insertions(+), 30 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index ae950263..5803db8c 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -82,6 +82,8 @@ namespace ztmu std::string getCurrentLine(); void setCurrentLine(const std::string& s); + void setMessageOnControlC(const std::string& msg); + std::string promptString; std::string contPromptString; std::string wrappedPromptString; @@ -107,6 +109,9 @@ namespace ztmu std::vector savedCurrentInput; bool uniqueHistory = true; size_t historyIdx = 0; + + bool wasAborted = false; + std::string uselessControlCMsg; }; } @@ -1005,12 +1010,13 @@ namespace detail refresh_line(st); } - static void cursor_end(State* st) + static void cursor_end(State* st, bool refresh = true) { st->cursor = displayedTextLength(getCurLine(st)); st->byteCursor = getCurLine(st).size(); - refresh_line(st); + if(refresh) + refresh_line(st); } @@ -1097,6 +1103,26 @@ namespace detail } } + // bound to C-k by default + static void delete_line_right(State* st) + { + if(st->byteCursor < st->lines[st->lineIdx].size()) + { + st->lines[st->lineIdx].erase(st->byteCursor); + refresh_line(st); + } + else if(st->lineIdx + 1 < st->lines.size()) + { + // just call delete_right, which will collapse this line. + delete_right(st); + } + } + + + + + + // we differentiate between _cursor_left() and cursor_left() -- the latter only moves along // a single line ("logical line"), while the latter will move to the previous line if the current // line is exhausted. user-inputs call cursor_left(), while internal users should call _cursor_left(). @@ -1394,7 +1420,8 @@ namespace detail constexpr char CTRL_C = '\x03'; constexpr char CTRL_D = '\x04'; constexpr char CTRL_E = '\x05'; - constexpr char BACKSPACE_ = '\x08'; + constexpr char CTRL_H = '\x08'; + constexpr char CTRL_K = '\x0B'; constexpr char ENTER = '\x0D'; constexpr char BACKSPACE = '\x7F'; @@ -1435,6 +1462,22 @@ namespace detail didSetSignalHandler = true; + auto commit_line = [&](bool refresh = true) { + // move the cursor to the end, refresh the line, then leave raw mode -- this makes sure + // that we leave the cursor in a nice place after the call to this function returns. + cursor_end(st, refresh); + + // see how many lines we need to go down. + size_t down = 0; + for(size_t i = st->lineIdx + 1; i < st->lines.size(); i++) + down += calculate_nwl(st, i, displayedTextLength(st->lines[i])); + + platform_write(moveCursorDown(down)); + platform_write(moveCursorLeft(9999)); + }; + + st->wasAborted = false; + bool eof = false; while(true) { @@ -1474,8 +1517,10 @@ namespace detail // and delete-left: when it is not... case CTRL_D: { // if the buffer is empty, then quit. - if(getCurLine(st).empty()) + if(getCurLine(st).empty() && st->lineIdx == 0) { + st->wasAborted = true; + eof = true; goto finish; } @@ -1494,28 +1539,37 @@ namespace detail } break; case CTRL_C: { - if(getCurLine(st).empty()) + st->wasAborted = true; + + if(st->lines.size() == 1 && st->lines[0].empty()) { - eof = true; - goto finish; + platform_write(st->uselessControlCMsg); + goto finish_now; } else { - auto old = getCurLine(st); - getCurLine(st).clear(); + commit_line(/* refresh: */ false); - st->cursor = 0; - st->byteCursor = 0; - refresh_line(st, &old); + // if we are multi-line, we can return eof. if not, just return empty. + if(st->lines.size() > 1) + eof = true; + + st->clear(); + goto finish_now; } } // backspace - case BACKSPACE: [[fallthrough]]; - case BACKSPACE_: { + case CTRL_H: [[fallthrough]]; + case BACKSPACE: { delete_left(st); } break; + case CTRL_K: { + // delete to end of line. + delete_line_right(st); + } break; + // time for some fun. case ESC: { // there should be at least two more! @@ -1693,25 +1747,14 @@ namespace detail finish: + commit_line(); + + finish_now: // restore the signal state, and reset the terminal to normal mode. currentStateForSignal = 0; if(didSetSignalHandler) sigaction(SIGWINCH, &old_sa, nullptr); - // move the cursor to the end, refresh the line, then leave raw mode -- this makes sure - // that we leave the cursor in a nice place after the call to this function returns. - { - cursor_end(st); - - // see how many lines we need to go down. - size_t down = 0; - for(size_t i = st->lineIdx + 1; i < st->lines.size(); i++) - down += calculate_nwl(st, i, displayedTextLength(st->lines[i])); - - platform_write(moveCursorDown(down)); - platform_write(moveCursorLeft(9999)); - } - leaveRawMode(); platform_write("\n"); @@ -1859,6 +1902,9 @@ namespace ztmu // well if you unique it then you'll only get one empty history entry, but still. void State::addPreviousInputToHistory() { + if(this->wasAborted) + return; + // now, if we need to unique the history, then erase (if any) the // prior occurrence of that item. we shouldn't really need to check for more // than one, because any previous call to addHistory() should have already @@ -1885,6 +1931,11 @@ namespace ztmu return this->history; } + void State::setMessageOnControlC(const std::string& msg) + { + this->uselessControlCMsg = msg; + } + std::optional State::read() { // clear. diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 59bc84ed..c96e5a92 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -38,6 +38,7 @@ namespace repl st.setPrompt(PROMPT_STRING); st.setContPrompt(CONT_PROMPT_STRING); st.setWrappedPrompt(WRAP_PROMPT_STRING); + st.setMessageOnControlC(zpr::sprint("%s(use %s:q%s to quit)%s", COLOUR_GREY_BOLD, COLOUR_GREEN, COLOUR_GREY_BOLD, COLOUR_RESET)); // we need to put this up here, so the handler can capture it. int indentLevel = 0; diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index a1e83fab..054825de 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -71,8 +71,6 @@ namespace repl bool processLine(const std::string& line) { - fprintf(stderr, "line: '%s'\n", line.c_str()); - std::string replName = ""; frontend::CollectorState collector; From 3fcd60c7d5b9007fcefce0ec58bb4f344982cbd4 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 13 Nov 2019 14:24:49 +0800 Subject: [PATCH 073/129] probably 75% done with ztmu for now --- source/frontend/lexer.cpp | 7 ++-- source/include/ztmu.h | 78 ++++++++++++++++++++------------------- source/repl/driver.cpp | 24 ++++++++---- 3 files changed, 61 insertions(+), 48 deletions(-) diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index 54d87886..d8dbf8cb 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -146,7 +146,8 @@ namespace lexer } string_view stream = lines[*line].substr(*offset); - if(stream.empty()) + skipWhitespace(stream, pos, offset); + if(stream.empty() || stream[0] == 0) { out->loc = pos; out->type = TokenType::EndOfFile; @@ -154,17 +155,15 @@ namespace lexer } - size_t read = 0; size_t unicodeLength = 0; - // first eat all whitespace - skipWhitespace(stream, pos, offset); Token& tok = *out; tok.loc = pos; tok.type = TokenType::Invalid; + fprintf(stderr, "stream: '%d'\n", stream[0]); // check compound symbols first. if(hasPrefix(stream, "//")) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 5803db8c..09b896a6 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -1491,26 +1491,25 @@ namespace detail ztmu_dbg("[%d]", static_cast(c)); switch(c) { - // enter - case ENTER: { - if(auto fn = st->keyHandlers[Key::ENTER]; fn) + case CTRL_C: { + st->wasAborted = true; + + if(st->lines.size() == 1 && st->lines[0].empty()) { - auto res = fn(st, Key::ENTER); - switch(res) - { - case HandlerAction::CONTINUE: - goto loop_top; + platform_write(st->uselessControlCMsg); + goto finish_now; + } + else + { + commit_line(/* refresh: */ false); - case HandlerAction::QUIT: - eof = true; - goto finish; + // if we are multi-line, we can return eof. if not, just return empty. + if(st->lines.size() > 1) + eof = true; - default: - break; - } + st->clear(); + goto finish_now; } - - goto finish; } // this is a little complex; control-D is apparently both EOF: when the buffer is empty, @@ -1530,6 +1529,8 @@ namespace detail } } break; + + case CTRL_A: { cursor_home(st); } break; @@ -1538,27 +1539,6 @@ namespace detail cursor_end(st); } break; - case CTRL_C: { - st->wasAborted = true; - - if(st->lines.size() == 1 && st->lines[0].empty()) - { - platform_write(st->uselessControlCMsg); - goto finish_now; - } - else - { - commit_line(/* refresh: */ false); - - // if we are multi-line, we can return eof. if not, just return empty. - if(st->lines.size() > 1) - eof = true; - - st->clear(); - goto finish_now; - } - } - // backspace case CTRL_H: [[fallthrough]]; case BACKSPACE: { @@ -1570,6 +1550,30 @@ namespace detail delete_line_right(st); } break; + // enter + case ENTER: { + if(auto fn = st->keyHandlers[Key::ENTER]; fn) + { + auto res = fn(st, Key::ENTER); + switch(res) + { + case HandlerAction::CONTINUE: + goto loop_top; + + case HandlerAction::QUIT: + eof = true; + goto finish; + + default: + break; + } + } + + goto finish; + } + + + // time for some fun. case ESC: { // there should be at least two more! diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index c96e5a92..2d3bde4e 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -69,14 +69,24 @@ namespace repl } else if(bool needmore = processLine(input); needmore) { - auto calc_indent = [](char c) -> int { - switch(c) + size_t last_indented_line = 0; + auto calc_indent = [&last_indented_line, &st](char c) -> int { + + // note: we use +1 so that last_indented_line is 1-indexed. this + // entire thing is so we don't get increasing indentation levels if + // we delete and re-enter on a brace. + if(last_indented_line == 0 || st.lineIdx + 1 > last_indented_line) { - case '{': [[fallthrough]]; - case '(': [[fallthrough]]; - case '[': [[fallthrough]]; - case ',': - return 1; + switch(c) + { + case '{': [[fallthrough]]; + case '(': [[fallthrough]]; + case '[': [[fallthrough]]; + case ',': { + last_indented_line = st.lineIdx + 1; + return 1; + } + } } return 0; From 4c87f3b2115ec641df4a554ad2c319af59bbffa6 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 13 Nov 2019 16:15:35 +0800 Subject: [PATCH 074/129] :t command, and nice help --- meson.build | 1 + source/frontend/lexer.cpp | 2 - source/include/repl.h | 21 +++- source/include/ztmu.h | 189 ++++++++++++++++++++---------------- source/repl/commands.cpp | 195 ++++++++++++++++++++++++++++++++++++++ source/repl/driver.cpp | 26 +++-- source/repl/execute.cpp | 84 ++++++++++------ 7 files changed, 387 insertions(+), 131 deletions(-) create mode 100644 source/repl/commands.cpp diff --git a/meson.build b/meson.build index 9b0f85f8..767ffda4 100644 --- a/meson.build +++ b/meson.build @@ -188,6 +188,7 @@ source_files = files([ 'source/repl/driver.cpp', 'source/repl/execute.cpp', + 'source/repl/commands.cpp', 'source/frontend/pts.cpp', 'source/frontend/file.cpp', diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index d8dbf8cb..b486a1a5 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -163,8 +163,6 @@ namespace lexer tok.loc = pos; tok.type = TokenType::Invalid; - fprintf(stderr, "stream: '%d'\n", stream[0]); - // check compound symbols first. if(hasPrefix(stream, "//")) { diff --git a/source/include/repl.h b/source/include/repl.h index fe60391f..55849b8c 100644 --- a/source/include/repl.h +++ b/source/include/repl.h @@ -5,16 +5,35 @@ #pragma once #include "defs.h" +#include + + namespace repl { + struct State; + void start(); void setupEnvironment(); + bool processLine(const std::string& line); + std::optional parseAndTypecheck(const std::string& line, bool* needmore); + + void runCommand(const std::string& command); + + // used to save/restore if we wanna do weird things. + void setEnvironment(State* st); + State* getEnvironment(); template static void error(const std::string& fmt, Args&&... args) { - fprintf(stderr, "%serror:%s %s\n", COLOUR_RED_BOLD, COLOUR_RESET, zpr::sprint(fmt, args...).c_str()); + fprintf(stderr, " %s*%s %s\n", COLOUR_RED_BOLD, COLOUR_RESET, zpr::sprint(fmt, args...).c_str()); + } + + template + static void log(const std::string& fmt, Args&&... args) + { + printf(" %s*%s %s\n", COLOUR_GREEN_BOLD, COLOUR_RESET, zpr::sprint(fmt, args...).c_str()); } } diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 09b896a6..0cf4b774 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -83,6 +83,7 @@ namespace ztmu void setCurrentLine(const std::string& s); void setMessageOnControlC(const std::string& msg); + void enableExitOnEmptyControlC(); std::string promptString; std::string contPromptString; @@ -111,8 +112,12 @@ namespace ztmu size_t historyIdx = 0; bool wasAborted = false; + bool emptyCtrlCExits = false; std::string uselessControlCMsg; }; + + size_t getTerminalWidth(); + size_t displayedTextLength(const std::string_view& str); } @@ -140,28 +145,19 @@ namespace detail int params[8]; }; - static void leaveRawMode(); - static void enterRawMode(); - - static int platform_read_one(char* c); - static int platform_read(char* c, size_t len); - static void platform_write(const char* s, size_t len); - static void platform_write(const std::string_view& sv); - - static std::string moveCursorUp(int n); - static std::string moveCursorDown(int n); - static std::string moveCursorLeft(int n); - static std::string moveCursorRight(int n); - static CursorPosition getCursorPosition(); - static std::string setCursorPosition(const CursorPosition& pos); - - static size_t displayedTextLength(const std::string_view& str); - static size_t parseEscapeSequence(const std::string_view& str); - - - - - + size_t getTerminalWidth(); + size_t displayedTextLength(const std::string_view& str); + bool read_line(State* st, int promptMode, std::string seed); + void cursor_left(State* st, bool refresh = true); + void cursor_right(State* st, bool refresh = true); + void refresh_line(State* st, std::string* oldLine = 0); + void cursor_up(State* st); + void cursor_down(State* st); + void cursor_home(State* st); + void cursor_end(State* st, bool refresh = true); + void delete_left(State* st); + void delete_right(State* st); + size_t convertCursorToByteCursor(const char* bytes, size_t cursor); @@ -178,7 +174,7 @@ namespace detail -#if ZTMU_CREATE_IMPL || true +#if ZTMU_CREATE_IMPL // || true // comes as a pair yo #include "zpr.h" @@ -229,7 +225,7 @@ namespace detail #endif - #if 1 + #if 0 template void ztmu_dbg(const char* fmt, Args&&... args) { @@ -257,6 +253,11 @@ namespace detail platform_write(sv.data(), sv.size()); } + static inline std::string moveCursorUp(int n); + static inline std::string moveCursorDown(int n); + static inline std::string moveCursorLeft(int n); + static inline std::string moveCursorRight(int n); + static inline std::string moveCursorUp(int n) { if(n == 0) return ""; @@ -312,13 +313,13 @@ namespace detail return ret; } - static std::string setCursorPosition(const CursorPosition& pos) - { - if(pos.x <= 0 || pos.y <= 0) - return ""; + // static std::string setCursorPosition(const CursorPosition& pos) + // { + // if(pos.x <= 0 || pos.y <= 0) + // return ""; - return zpr::sprint("%s%d;%dH", CSI, pos.y, pos.x); - } + // return zpr::sprint("%s%d;%dH", CSI, pos.y, pos.x); + // } @@ -412,7 +413,7 @@ namespace detail } - static size_t displayedTextLength(const std::string_view& str) + inline size_t displayedTextLength(const std::string_view& str) { auto utf8_len = [](const char* begin, const char* end) -> size_t { size_t len = 0; @@ -508,7 +509,7 @@ namespace detail isInRawMode = true; } - static size_t getTerminalWidth() + inline size_t getTerminalWidth() { #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; @@ -593,7 +594,7 @@ namespace detail return i; } - static size_t convertCursorToByteCursor(const char* bytes, size_t cursor) + inline size_t convertCursorToByteCursor(const char* bytes, size_t cursor) { size_t bc = 0; for(size_t i = 0; i < cursor; i++) @@ -619,22 +620,22 @@ namespace detail return { cursor, byteCursor }; } - static std::pair calculate_right_codepoints(int n, const std::string_view& sv, size_t cursor, size_t byteCursor) - { - for(int i = 0; i < n; i++) - { - if(byteCursor < sv.size()) - { - auto x = byteCursor; - auto l = findEndOfUTF8CP(reinterpret_cast(sv.data()), x); + // static std::pair calculate_right_codepoints(int n, const std::string_view& sv, size_t cursor, size_t byteCursor) + // { + // for(int i = 0; i < n; i++) + // { + // if(byteCursor < sv.size()) + // { + // auto x = byteCursor; + // auto l = findEndOfUTF8CP(reinterpret_cast(sv.data()), x); - byteCursor += (l - x + 1); - cursor += 1; - } - } + // byteCursor += (l - x + 1); + // cursor += 1; + // } + // } - return { cursor, byteCursor }; - } + // return { cursor, byteCursor }; + // } @@ -933,7 +934,7 @@ namespace detail } } - static void refresh_line(State* st, std::string* oldLine = 0) + inline void refresh_line(State* st, std::string* oldLine) { // refresh the top line manually: @@ -1002,7 +1003,7 @@ namespace detail } - static void cursor_home(State* st) + inline void cursor_home(State* st) { st->cursor = 0; st->byteCursor = 0; @@ -1010,7 +1011,7 @@ namespace detail refresh_line(st); } - static void cursor_end(State* st, bool refresh = true) + inline void cursor_end(State* st, bool refresh) { st->cursor = displayedTextLength(getCurLine(st)); st->byteCursor = getCurLine(st).size(); @@ -1022,7 +1023,7 @@ namespace detail // note: internal code doesn't use delete_left or delete_right, so we don't need two // versions that handle lines. - static void delete_left(State* st) + inline void delete_left(State* st) { if(st->cursor > 0 && getCurLine(st).size() > 0) { @@ -1068,7 +1069,7 @@ namespace detail } } - static void delete_right(State* st) + inline void delete_right(State* st) { if(st->byteCursor < getCurLine(st).size()) { @@ -1144,7 +1145,7 @@ namespace detail refresh_line(st); } - static void cursor_left(State* st, bool refresh = true) + inline void cursor_left(State* st, bool refresh) { if(st->cursor > 0) { @@ -1186,7 +1187,7 @@ namespace detail refresh_line(st); } - static void cursor_right(State* st, bool refresh = true) + inline void cursor_right(State* st, bool refresh) { if(st->byteCursor < getCurLine(st).size()) { @@ -1239,7 +1240,7 @@ namespace detail refresh_line(st); } - static void cursor_up(State* st) + inline void cursor_up(State* st) { if(st->cursor > 0 && st->wrappedLineIdx > 0) { @@ -1333,7 +1334,7 @@ namespace detail } } - static void cursor_down(State* st) + inline void cursor_down(State* st) { if(st->byteCursor < getCurLine(st).size() && (st->lines.size() == 1 || st->wrappedLineIdx + 1 < st->cachedNWLForCurrentLine)) { @@ -1414,7 +1415,7 @@ namespace detail // this is ugly!!! static State* currentStateForSignal = 0; - static bool read_line(State* st, int promptMode, std::string seed) + inline bool read_line(State* st, int promptMode, std::string seed) { constexpr char CTRL_A = '\x01'; constexpr char CTRL_C = '\x03'; @@ -1496,8 +1497,17 @@ namespace detail if(st->lines.size() == 1 && st->lines[0].empty()) { - platform_write(st->uselessControlCMsg); - goto finish_now; + if(st->emptyCtrlCExits) + { + st->wasAborted = true; + eof = true; + goto finish; + } + else + { + platform_write(st->uselessControlCMsg); + goto finish_now; + } } else { @@ -1774,7 +1784,17 @@ namespace detail namespace ztmu { - void State::clear() + inline size_t displayedTextLength(const std::string_view& str) + { + return detail::displayedTextLength(str); + } + + inline size_t getTerminalWidth() + { + return detail::getTerminalWidth(); + } + + inline void State::clear() { // reset the thing. this->savedCurrentInput.clear(); @@ -1785,25 +1805,25 @@ namespace ztmu this->wrappedLineIdx = 0; } - void State::setPrompt(const std::string& prompt) + inline void State::setPrompt(const std::string& prompt) { this->promptString = prompt; this->normPL = detail::displayedTextLength(prompt); } - void State::setContPrompt(const std::string& prompt) + inline void State::setContPrompt(const std::string& prompt) { this->contPromptString = prompt; this->contPL = detail::displayedTextLength(prompt); } - void State::setWrappedPrompt(const std::string& prompt) + inline void State::setWrappedPrompt(const std::string& prompt) { this->wrappedPromptString = prompt; this->wrapPL = detail::displayedTextLength(prompt); } - void State::move_cursor_left(int n) + inline void State::move_cursor_left(int n) { if(n < 0) move_cursor_right(-n); @@ -1813,7 +1833,7 @@ namespace ztmu detail::refresh_line(this); } - void State::move_cursor_right(int n) + inline void State::move_cursor_right(int n) { if(n < 0) move_cursor_left(-n); @@ -1823,7 +1843,7 @@ namespace ztmu detail::refresh_line(this); } - void State::move_cursor_up(int n) + inline void State::move_cursor_up(int n) { if(n < 0) move_cursor_down(-n); @@ -1833,7 +1853,7 @@ namespace ztmu detail::refresh_line(this); } - void State::move_cursor_down(int n) + inline void State::move_cursor_down(int n) { if(n < 0) move_cursor_up(-n); @@ -1844,17 +1864,17 @@ namespace ztmu } - void State::move_cursor_home() + inline void State::move_cursor_home() { detail::cursor_home(this); } - void State::move_cursor_end() + inline void State::move_cursor_end() { detail::cursor_end(this); } - void State::delete_left(int n) + inline void State::delete_left(int n) { if(n <= 0) return; @@ -1862,7 +1882,7 @@ namespace ztmu detail::delete_left(this); } - void State::delete_right(int n) + inline void State::delete_right(int n) { if(n <= 0) return; @@ -1870,22 +1890,22 @@ namespace ztmu detail::delete_right(this); } - void State::setKeyHandler(Key k, std::function handler) + inline void State::setKeyHandler(Key k, std::function handler) { this->keyHandlers[k] = handler; } - void State::unsetKeyHandler(Key k) + inline void State::unsetKeyHandler(Key k) { this->keyHandlers[k] = std::function(); } - std::string State::getCurrentLine() + inline std::string State::getCurrentLine() { return this->lines[this->lineIdx]; } - void State::setCurrentLine(const std::string& s) + inline void State::setCurrentLine(const std::string& s) { auto old = this->lines[this->lineIdx]; this->lines[this->lineIdx] = s; @@ -1897,14 +1917,14 @@ namespace ztmu detail::refresh_line(this, &old); } - void State::useUniqueHistory(bool enable) + inline void State::useUniqueHistory(bool enable) { this->uniqueHistory = enable; } // your responsibility to check if the input was empty! // well if you unique it then you'll only get one empty history entry, but still. - void State::addPreviousInputToHistory() + inline void State::addPreviousInputToHistory() { if(this->wasAborted) return; @@ -1924,23 +1944,28 @@ namespace ztmu this->history.push_back(this->lines); } - void State::loadHistory(const std::vector>& h) + inline void State::loadHistory(const std::vector>& h) { this->history = h; } // it's up to you to serialise it! - std::vector> State::getHistory() + inline std::vector> State::getHistory() { return this->history; } - void State::setMessageOnControlC(const std::string& msg) + inline void State::setMessageOnControlC(const std::string& msg) { this->uselessControlCMsg = msg; } - std::optional State::read() + inline void State::enableExitOnEmptyControlC() + { + this->emptyCtrlCExits = true; + } + + inline std::optional State::read() { // clear. this->clear(); @@ -1950,7 +1975,7 @@ namespace ztmu else return this->lines.back(); } - std::optional> State::readContinuation(const std::string& seed) + inline std::optional> State::readContinuation(const std::string& seed) { // don't clear this->lineIdx++; diff --git a/source/repl/commands.cpp b/source/repl/commands.cpp new file mode 100644 index 00000000..6974971c --- /dev/null +++ b/source/repl/commands.cpp @@ -0,0 +1,195 @@ +// commands.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include + +#include "defs.h" +#include "repl.h" +#include "ztmu.h" + +#include "sst.h" +#include "ir/type.h" + +namespace repl +{ + static void print_help(); + static void print_type(const std::string& expr); + + void runCommand(const std::string& s) + { + if(s == "q") + { + repl::log("exiting repl"); + + exit(0); + } + else if(s == "reset") + { + repl::setupEnvironment(); + repl::log("environment reset"); + } + else if(s == "help" || s == "?") + { + print_help(); + } + else if(s.find("t ") == 0) + { + print_type(s.substr(2)); + } + else + { + repl::error("invalid command '%s'", s); + } + } + + + + + + + static void print_type(const std::string& line) + { + bool needmore = false; + auto stmt = repl::parseAndTypecheck(line, &needmore); + if(needmore) + { + repl::error("':t' does not support continuations"); + } + else if(!stmt) + { + repl::error("invalid expression"); + } + else if(auto expr = dcast(sst::Expr, *stmt)) + { + zpr::println("%s%s%s: %s", COLOUR_GREY_BOLD, line, COLOUR_RESET, expr->type); + } + else + { + repl::error("'%s' is not an expression", (*stmt)->readableName); + } + } + + + + static std::vector helpLines = { + zpr::sprint(""), + zpr::sprint("%s*%s overview %s*%s", COLOUR_GREEN_BOLD, COLOUR_RESET, COLOUR_GREEN_BOLD, COLOUR_RESET), + zpr::sprint("\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e"), + zpr::sprint("The repl accepts Flax expressions and statements; press enter to evaluate the currently entered" + " input. If the input was incomplete (eg. ending with a '{'), then the repl will enter a multi-line continuation" + " mode. In either case, use the standard keybindings (arrow keys, home/end, etc.) to navigate."), + zpr::sprint(""), + zpr::sprint("Any definitions (eg. variables, functions) will be treated as if they were declared at global" + " scope, while expressions and statements (eg. loops, arithmetic) will be treated as if they were" + " written in a function body."), + zpr::sprint(""), + zpr::sprint("Expressions with values (eg. 3 + 1) will be given monotonic identifiers (eg. %s_0%s, %s_1%s) that" + " can be used like any other identifier in code.", COLOUR_BLUE, COLOUR_RESET, COLOUR_BLUE, COLOUR_RESET), + zpr::sprint(""), + zpr::sprint("Commands begin with ':', and modify the state of the repl or perform other meta-actions."), + + zpr::sprint(""), + zpr::sprint("%s*%s commands %s*%s", COLOUR_GREEN_BOLD, COLOUR_RESET, COLOUR_GREEN_BOLD, COLOUR_RESET), + zpr::sprint("\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e"), + zpr::sprint(" :? / :help - display help (this listing)"), + zpr::sprint(" :q - quit the repl"), + zpr::sprint(" :reset - reset the environment, discarding all existing definitions"), + zpr::sprint(" :t - display the type of an expression"), + }; + + static void print_help() + { + auto split_words = [](std::string& s) -> std::vector { + std::vector ret; + + size_t word_start = 0; + for(size_t i = 0; i < s.size(); i++) + { + if(s[i] == ' ') + { + ret.push_back(std::string_view(s.c_str() + word_start, i - word_start)); + word_start = i + 1; + } + else if(s[i] == '-') + { + ret.push_back(std::string_view(s.c_str() + word_start, i - word_start + 1)); + word_start = i + 1; + } + } + + ret.push_back(std::string_view(s.c_str() + word_start)); + return ret; + }; + + constexpr const char* LEFT_MARGIN = " "; + constexpr const char* RIGHT_MARGIN = " "; + + auto tw = ztmu::getTerminalWidth(); + tw = std::min(tw, tw - strlen(LEFT_MARGIN) - strlen(RIGHT_MARGIN)); + + auto disp_len = ztmu::displayedTextLength; + + for(auto& l : helpLines) + { + size_t remaining = tw; + + // sighs. + auto ss = std::stringstream(); + ss << LEFT_MARGIN; + + // each "line" is actually a paragraph. we want to be nice, so pad on the right by a few spaces + // and hyphenate split words. + + // first split into words + auto words = split_words(l); + for(const auto& word : words) + { + // printf("w: '%s' -- ", std::string(word).c_str()); + + auto len = disp_len(word); + if(remaining >= len) + { + ss << word << (word.back() != '-' ? " " : ""); + + // printf("norm (%zu)", remaining); + remaining -= len; + + if(remaining > 0) + { + remaining -= 1; + } + else + { + ss << "\n" << LEFT_MARGIN; + remaining = tw; + } + + // printf("(%zu)\n", remaining); + } + else if(remaining < 3 || len < 5) + { + // for anything less than 5 chars, put it on the next line -- don't hyphenate. + ss << "\n" << LEFT_MARGIN << word << (word.back() != '-' ? " " : ""); + + // printf("next (%zu)", remaining); + remaining = tw - (len + 1); + // printf("(%zu)\n", remaining); + } + else + { + // split it. + ss << word.substr(0, remaining - 2) << "-" << "\n"; + ss << LEFT_MARGIN << word.substr(remaining - 2) << " "; + + // printf("split (%zu)", remaining); + remaining = tw - word.substr(remaining - 2).size(); + // printf("(%zu)\n", remaining); + } + } + + zpr::println(ss.str()); + } + } + +} diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 2d3bde4e..bad472c5 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -12,15 +12,7 @@ namespace repl { - static void runCommand(const std::string& s) - { - if(s == "q") exit(0); - else if(s == "reset") { zpr::println("resetting environment..."); setupEnvironment(); } - else if(s == "help") repl::error("no help implemented. ggwp."); - else repl::error("invalid command '%s'.", s); - } - - static constexpr const char* PROMPT_STRING = COLOUR_BLUE " * " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; + static constexpr const char* PROMPT_STRING = COLOUR_BLUE_BOLD " * " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; static constexpr const char* WRAP_PROMPT_STRING = COLOUR_GREY_BOLD " |" COLOUR_RESET " "; static constexpr const char* CONT_PROMPT_STRING = COLOUR_YELLOW_BOLD ".. " COLOUR_GREY_BOLD ">" COLOUR_RESET " "; @@ -30,9 +22,9 @@ namespace repl void start() { zpr::println("flax repl -- version %s", frontend::getVersion()); - zpr::println("type :help for help\n"); + zpr::println("type %s:?%s for help\n", COLOUR_GREEN_BOLD, COLOUR_RESET); - setupEnvironment(); + repl::setupEnvironment(); auto st = ztmu::State(); st.setPrompt(PROMPT_STRING); @@ -40,6 +32,11 @@ namespace repl st.setWrappedPrompt(WRAP_PROMPT_STRING); st.setMessageOnControlC(zpr::sprint("%s(use %s:q%s to quit)%s", COLOUR_GREY_BOLD, COLOUR_GREEN, COLOUR_GREY_BOLD, COLOUR_RESET)); + // temporary. + st.enableExitOnEmptyControlC(); + + + // we need to put this up here, so the handler can capture it. int indentLevel = 0; @@ -64,10 +61,9 @@ namespace repl if(input[0] == ':') { - runCommand(input.substr(1)); - printf("\n"); + repl::runCommand(input.substr(1)); } - else if(bool needmore = processLine(input); needmore) + else if(bool needmore = repl::processLine(input); needmore) { size_t last_indented_line = 0; auto calc_indent = [&last_indented_line, &st](char c) -> int { @@ -108,7 +104,7 @@ namespace repl auto input = join_lines(*lines); indentLevel += calc_indent(input.back()); - needmore = processLine(input); + needmore = repl::processLine(input); if(!needmore) break; diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index 054825de..ba0ceb0e 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -69,7 +69,18 @@ namespace repl state = new State(); } - bool processLine(const std::string& line) + void setEnvironment(State* st) + { + state = st; + } + + State* getEnvironment() + { + return state; + } + + + std::optional parseAndTypecheck(const std::string& line, bool* needmore) { std::string replName = ""; @@ -83,24 +94,28 @@ namespace repl auto st = parser::State(lexResult.tokens); auto _stmt = parser::parseStmt(st, /* exprs: */ true); + *needmore = false; if(_stmt.needsMoreTokens()) { - return true; + *needmore = true; + return std::nullopt; } else if(_stmt.isError()) { _stmt.err()->post(); - return false; - } - // there's no need to fiddle with AST-level trees -- once we typecheck it, - // it will store the relevant state into the TypecheckState. + return std::nullopt; + } + else { auto stmt = _stmt.val(); // ugh. auto tcr = TCResult(reinterpret_cast(0)); + // there's no need to fiddle with AST-level trees -- once we typecheck it, + // it will store the relevant state into the TypecheckState. + try { tcr = stmt->typecheck(state->fs); @@ -110,51 +125,58 @@ namespace repl ee.err->post(); printf("\n"); - return false; + return std::nullopt; } - - if(tcr.isError()) { tcr.error()->post(); printf("\n"); - - return false; } else if(!tcr.isParametric() && !tcr.isDummy()) { - // copy some stuff over. - state->cs->typeDefnMap = state->fs->typeDefnMap; + return tcr.stmt(); + } - // ok, we have a thing. try to run it. + return std::nullopt; + } + } - auto value = magicallyRunExpressionAtCompileTime(state->cs, tcr.stmt(), nullptr, - Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name)); - if(value) - { - // if it was an expression, then give it a name so we can refer to it later. - auto init = util::pool(Location(), value->getType()); - init->rawValue = CGResult(value); + bool processLine(const std::string& line) + { + bool needmore = false; + auto stmt = repl::parseAndTypecheck(line, &needmore); + if(!stmt) + return needmore; - auto vardef = util::pool(Location()); - vardef->type = init->type; - vardef->id = Identifier(zpr::sprint("_%d", state->varCounter++), IdKind::Name); - vardef->global = true; - vardef->init = init; + { + // copy some stuff over. + state->cs->typeDefnMap = state->fs->typeDefnMap; - state->fs->stree->addDefinition(vardef->id.name, vardef); + // ok, we have a thing. try to run it. + auto value = magicallyRunExpressionAtCompileTime(state->cs, *stmt, nullptr, + Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name)); - printf("%s\n", zpr::sprint("%s: %s = %s", vardef->id.name, value->getType(), value->str()).c_str()); - } - } - } + if(value) + { + // if it was an expression, then give it a name so we can refer to it later. + auto init = util::pool(Location(), value->getType()); + init->rawValue = CGResult(value); + auto vardef = util::pool(Location()); + vardef->type = init->type; + vardef->id = Identifier(zpr::sprint("_%d", state->varCounter++), IdKind::Name); + vardef->global = true; + vardef->init = init; + state->fs->stree->addDefinition(vardef->id.name, vardef); + printf("%s\n", zpr::sprint("%s: %s = %s", vardef->id.name, value->getType(), value->str()).c_str()); + } + } return false; From 90f5a9aaa743785d8dd9b65c9e7cb33b862303d1 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 15 Nov 2019 22:35:39 +0800 Subject: [PATCH 075/129] add repl history, fix various things --- README.md | 2 + build/supertiny.flx | 2 + source/backend/interp/driver.cpp | 2 +- source/codegen/codegenstate.cpp | 59 ++++++++++++------ source/codegen/directives.cpp | 7 ++- source/codegen/variable.cpp | 12 ++-- source/fir/ConstantValue.cpp | 16 ++++- source/fir/Instruction.cpp | 4 +- source/fir/Types/StructType.cpp | 4 ++ source/fir/interp/interpreter.cpp | 31 +++++++-- source/frontend/errors.cpp | 14 +++-- source/frontend/parser/type.cpp | 8 ++- source/include/codegen.h | 18 +++++- source/include/ir/interp.h | 2 +- source/include/ir/type.h | 1 + source/include/parser_internal.h | 2 +- source/include/repl.h | 11 +++- source/include/ztmu.h | 19 +++--- source/platform/platform.cpp | 17 ++--- source/repl/commands.cpp | 21 ++++--- source/repl/driver.cpp | 9 ++- source/repl/execute.cpp | 15 ++++- source/repl/history.cpp | 100 ++++++++++++++++++++++++++++++ 23 files changed, 298 insertions(+), 78 deletions(-) create mode 100644 source/repl/history.cpp diff --git a/README.md b/README.md index 88d901e1..5accda4d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ A low level, general-purpose language with high level syntax and expressibility.      [![Build status](https://ci.appveyor.com/api/projects/status/c9cmm08t27ef1hji/branch/develop?svg=true)](https://ci.appveyor.com/project/zhiayang/flax/branch/develop)      +[![Build status](https://zhiayang.semaphoreci.com/badges/flax.svg)](zhiayang.semaphoreci.com/projects/flax) +     [![codecov](https://codecov.io/gh/flax-lang/flax/branch/develop/graph/badge.svg)](https://codecov.io/gh/flax-lang/flax)      [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) diff --git a/build/supertiny.flx b/build/supertiny.flx index 0a8cd055..fcd316a4 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -2,6 +2,7 @@ // Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. + export supertiny import libc as _ @@ -259,3 +260,4 @@ class B : A + diff --git a/source/backend/interp/driver.cpp b/source/backend/interp/driver.cpp index e2baa0e5..4d9433bc 100644 --- a/source/backend/interp/driver.cpp +++ b/source/backend/interp/driver.cpp @@ -51,7 +51,7 @@ namespace backend void FIRInterpBackend::performCompilation() { this->is = new InterpState(this->compiledData.module); - this->is->initialise(); + this->is->initialise(/* runGlobalInit:*/ true); // it suffices to compile just the entry function. this->is->compileFunction(this->compiledData.module->getEntryFunction()); diff --git a/source/codegen/codegenstate.cpp b/source/codegen/codegenstate.cpp index 28c38acb..efbe0628 100644 --- a/source/codegen/codegenstate.cpp +++ b/source/codegen/codegenstate.cpp @@ -339,50 +339,71 @@ namespace cgn return this->isInsideGlobalInitFunc; } - fir::IRBlock* CodegenState::enterGlobalInitFunction() + fir::IRBlock* CodegenState::enterGlobalInitFunction(fir::GlobalValue* val) { + if(this->isInsideGlobalInitFunc) + error(this->loc(), "unsynchronised use of global init function!!! (entering when already inside)"); + auto ret = this->irb.getCurrentBlock(); - if(!this->globalInitFunc) { - fir::Function* func = this->module->getOrCreateFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION), + fir::Function* func = this->module->getOrCreateFunction( + util::obfuscateIdentifier(zpr::sprint("%s_piece_%d", strs::names::GLOBAL_INIT_FUNCTION, this->globalInitPieces.size())), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); - fir::IRBlock* entry = this->irb.addNewBlockInFunction("entry", func); - this->irb.setCurrentBlock(entry); + auto b = this->irb.addNewBlockInFunction("b", func); + this->irb.setCurrentBlock(b); - this->globalInitFunc = func; + this->globalInitPieces.push_back(std::make_pair(val, func)); } - iceAssert(this->globalInitFunc); - this->irb.setCurrentBlock(this->globalInitFunc->getBlockList().back()); - this->isInsideGlobalInitFunc = true; return ret; } void CodegenState::leaveGlobalInitFunction(fir::IRBlock* restore) { + if(!this->isInsideGlobalInitFunc) + error(this->loc(), "unsynchronised use of global init function!!! (leaving when not inside)"); + + // terminate the current function. + this->irb.ReturnVoid(); + this->irb.setCurrentBlock(restore); this->isInsideGlobalInitFunc = false; } void CodegenState::finishGlobalInitFunction() { - auto r = this->enterGlobalInitFunction(); - - // ok, here's a thing -- if the current block doesn't end with a return, then we insert it. - // this lets us keep calling finishGlobalInitFunction() without repercussions, even if we - // already "finished" the constructor function. this workaround is used to make our life - // simpler when doing compile-time execution. + if(this->finalisedGlobalInitFunction != 0) + { + // clear all the blocks from it. + for(auto b : this->finalisedGlobalInitFunction->getBlockList()) + delete b; - if(const auto& instrs = this->irb.getCurrentBlock()->getInstructions(); - instrs.empty() || instrs.back()->opKind != fir::OpKind::Value_Return) + this->finalisedGlobalInitFunction->getBlockList().clear(); + } + else { - this->irb.ReturnVoid(); + this->finalisedGlobalInitFunction = this->module->getOrCreateFunction( + util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION), + fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); } - this->leaveGlobalInitFunction(r); + auto restore = this->irb.getCurrentBlock(); + + // ok, now we can do some stuff. + // what we wanna do is just call all the "piece" global init functions that we made with enter/leave. + // but, this function has no blocks (either cos it's new, or we deleted them all). so, make one. + auto blk = this->irb.addNewBlockInFunction("entry", this->finalisedGlobalInitFunction); + this->irb.setCurrentBlock(blk); + + for(auto piece : this->globalInitPieces) + this->irb.Call(piece.second); + + // ok now return + this->irb.ReturnVoid(); + this->irb.setCurrentBlock(restore); } } diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp index c630af4b..bdfdf034 100644 --- a/source/codegen/directives.cpp +++ b/source/codegen/directives.cpp @@ -68,7 +68,7 @@ fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, s if(!is) { is = new fir::interp::InterpState(cs->module); - is->initialise(); + is->initialise(/* runGlobalInit: */ true); needToFinalise = true; } @@ -81,9 +81,10 @@ fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, s } if(needToFinalise) + { is->finalise(); - - delete is; + delete is; + } } return ret; diff --git a/source/codegen/variable.cpp b/source/codegen/variable.cpp index bbf381f7..2409920f 100644 --- a/source/codegen/variable.cpp +++ b/source/codegen/variable.cpp @@ -35,17 +35,19 @@ CGResult sst::VarDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->global) { - auto rest = cs->enterGlobalInitFunction(); + //* note: we declare it as not-immutable here to make it easier to set things, + //* but otherwise we make it immutable again below after init. + auto glob = cs->module->createGlobalVariable(this->id, this->type, false, + this->visibility == VisibilityLevel::Public ? fir::LinkageType::External : fir::LinkageType::Internal); + + auto rest = cs->enterGlobalInitFunction(glob); + - // else fir::Value* res = 0; if(this->init) res = this->init->codegen(cs, this->type).value; else res = cs->getDefaultValue(this->type); - //* note: we declare it as not-immutable here to make it easier to set things, but otherwise we make it immutable again below after init. - auto glob = cs->module->createGlobalVariable(this->id, this->type, false, - this->visibility == VisibilityLevel::Public ? fir::LinkageType::External : fir::LinkageType::Internal); if(auto cv = dcast(fir::ConstantValue, res); cv && cv->getType() == this->type) { diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 1e1b8a0d..2b21f711 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -293,9 +293,21 @@ namespace fir std::string ConstantStruct::str() { + auto sty = this->getType()->toStructType(); + util::hash_map names; + for(const auto& p : sty->getIndexMap()) + names[p.second] = p.first; + std::string ret = this->getType()->str() + " {\n"; - for(auto x : this->members) - ret += " " + x->str() + "\n"; + + for(size_t i = 0; i < sty->getElementCount(); i++) + { + auto v = this->members[i]->str(); + auto t = sty->getElementN(i); + auto n = names[i]; + + ret += zpr::sprint(" %s: %s = %s\n", n, t, v); + } return ret + "}"; } diff --git a/source/fir/Instruction.cpp b/source/fir/Instruction.cpp index d261bc69..12b4639c 100644 --- a/source/fir/Instruction.cpp +++ b/source/fir/Instruction.cpp @@ -223,8 +223,8 @@ namespace fir } else if(ConstantArraySlice* cas = dcast(ConstantArraySlice, op)) { - ops += "(const slice %" + std::to_string(op->id) + ", %" + std::to_string(cas->getData()->id) + ", %" - + std::to_string(cas->getLength()->id) + " :: " + op->getType()->str(); + ops += "(const slice %" + std::to_string(op->id) + ": ptr: %" + std::to_string(cas->getData()->id) + ", len: %" + + std::to_string(cas->getLength()->id) + ") :: " + op->getType()->str(); } else if(dcast(ConstantValue, op)) { diff --git a/source/fir/Types/StructType.cpp b/source/fir/Types/StructType.cpp index 1e9c5c07..0fe99ad8 100644 --- a/source/fir/Types/StructType.cpp +++ b/source/fir/Types/StructType.cpp @@ -121,6 +121,10 @@ namespace fir return this->implTraits; } + const util::hash_map& StructType::getIndexMap() + { + return this->indexMap; + } void StructType::setBody(const std::vector>& members) diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 3bef5bb3..1c86e9b4 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -443,8 +443,9 @@ namespace interp delete[] p; } - void InterpState::initialise() + void InterpState::initialise(bool runGlobalInit) { + iceAssert(this->module); for(const auto& [ str, glob ] : this->module->_getGlobalStrings()) { auto val = makeValue(glob); @@ -477,7 +478,7 @@ namespace interp setValueRaw(this, &ret, &buffer, sizeof(void*)); ret.globalValTracker = glob; - this->globals[glob] = { ret, false }; + this->globals[glob] = { ret, true }; } for(const auto& [ id, intr ] : this->module->_getIntrinsicFunctions()) @@ -518,8 +519,11 @@ namespace interp } #endif - // we need to run the global constructor function! - if(auto gif = this->module->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION))) + printf("module: %s\n", this->module->print().c_str()); + + // truth be told it'll be more efficient to only get the function after checking runGlobalInit, but... meh. + if(auto gif = this->module->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION)); + runGlobalInit && gif) { auto cgif = this->compileFunction(gif); this->runFunction(cgif, { }); @@ -534,15 +538,22 @@ namespace interp { for(const auto [ id, glob ] : this->module->_getGlobals()) { + // printf("global: %s\n", id.str().c_str()); + // by right we are not supposed to add (or even change the FIR module at all) between calling // initialise() and finalise(), but be defensive a bit. if(auto it = this->globals.find(glob); it != this->globals.end()) { + // printf("found: %s\n", id.str().c_str()); + // only write-back if we modified the global. if(it->second.second) { auto val = loadFromPtr(it->second.first, it->first->getType()); - glob->setInitialValue(this->unwrapInterpValueIntoConstant(val)); + auto x = this->unwrapInterpValueIntoConstant(val); + + printf("write-back: %s = %s\n", id.name.c_str(), x->str().c_str()); + glob->setInitialValue(x); } } } @@ -2638,10 +2649,18 @@ namespace interp return fir::ConstantDynamicArray::get(ty->toDynamicArrayType(), ptr, len, cap); } + else if(ty->isStructType()) + { + auto sty = ty->toStructType(); + std::vector vals = extractValueList(val, sty->getElements()); + + return fir::ConstantStruct::get(sty, vals); + } #undef gav - error("interp: cannot unwrap type '%s'", ty); + warn("interp: cannot unwrap type '%s'", ty); + return fir::ConstantValue::getZeroValue(ty); } } } diff --git a/source/frontend/errors.cpp b/source/frontend/errors.cpp index c310bac1..8fb566d7 100644 --- a/source/frontend/errors.cpp +++ b/source/frontend/errors.cpp @@ -125,13 +125,19 @@ static std::string getSpannedContext(const Location& loc, const std::vector%s ", COLOUR_CYAN_BOLD, COLOUR_RESET); spanscopy[i].msg = remaining.substr(segment.length()); @@ -389,7 +395,7 @@ void SpanError::post() } else { - cursor += 1 + strprinterrf("%s", spaces(2 + num_width + col - adjust - cursor)); + cursor += 1 + strprinterrf("%s", spaces((LEFT_PADDING - 1) + 1 + col - adjust - cursor)); strprinterrf("%s|%s", COLOUR_CYAN_BOLD, COLOUR_RESET); } } diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index a813d1e0..98e400e0 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -165,7 +165,7 @@ namespace parser - StructDefn* parseStruct(State& st, bool nameless) + PResult parseStruct(State& st, bool nameless) { static size_t anon_counter = 0; @@ -233,6 +233,8 @@ namespace parser while(st.front() != TT::RBrace) { st.skipWS(); + if(!st.hasTokens()) + return PResult::insufficientTokensError(); if(st.front() == TT::Identifier) { @@ -288,7 +290,7 @@ namespace parser } else { - error(st.loc(), "unexpected token '%s' inside struct body", st.front().str()); + error(st.loc(), "unexpected token '%s' (%d) inside struct body", st.front().str(), st.front().type); } index++; @@ -836,7 +838,7 @@ namespace parser } else if(st.front() == TT::Struct) { - auto str = parseStruct(st, /* nameless: */ true); + auto str = parseStruct(st, /* nameless: */ true).val(); st.anonymousTypeDefns.push_back(str); return pts::NamedType::create(str->loc, str->name); diff --git a/source/include/codegen.h b/source/include/codegen.h index d1fea629..29fac0bc 100644 --- a/source/include/codegen.h +++ b/source/include/codegen.h @@ -57,6 +57,7 @@ namespace cgn std::vector raiiValues; }; + struct CodegenState { enum class OperatorFn @@ -80,7 +81,6 @@ namespace cgn util::hash_map valueMap; std::vector methodSelfStack; - fir::Function* globalInitFunc = 0; util::hash_map methodList; @@ -149,12 +149,26 @@ namespace cgn fir::Function* getOrDeclareLibCFunction(std::string name); + bool isInsideGlobalInitFunc = false; + + // this one holds the finalised global initialiser function -- this one is supposed to + // call all the pieces; we always regenerate this function when we call finishGlobalInitFunction(). + fir::Function* finalisedGlobalInitFunction = 0; + + // this is getting a bit complicated. this holds each "piece" of the global init function, + // where each piece probably corresponds to the initialisation of a single global value. + std::vector> globalInitPieces; + bool isWithinGlobalInitFunction(); - fir::IRBlock* enterGlobalInitFunction(); + fir::IRBlock* enterGlobalInitFunction(fir::GlobalValue* val); void leaveGlobalInitFunction(fir::IRBlock* restore); void finishGlobalInitFunction(); + + + + void generateDecompositionBindings(const DecompMapping& bind, CGResult rhs, bool allowref); util::hash_map getNameIndexMap(sst::FunctionDefn* fd); diff --git a/source/include/ir/interp.h b/source/include/ir/interp.h index d987bb4a..707fc67a 100644 --- a/source/include/ir/interp.h +++ b/source/include/ir/interp.h @@ -70,7 +70,7 @@ namespace fir InterpState(fir::Module* mod); ~InterpState(); - void initialise(); + void initialise(bool runGlobalInit); void finalise(); interp::Function& compileFunction(fir::Function* fn); diff --git a/source/include/ir/type.h b/source/include/ir/type.h index ee759586..cdc1a962 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -697,6 +697,7 @@ namespace fir void addTraitImpl(TraitType* trt); bool implementsTrait(TraitType* trt); std::vector getImplementedTraits(); + const util::hash_map& getIndexMap(); virtual std::string str() override; virtual std::string encodedStr() override; diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index acaa057a..12d5c782 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -461,7 +461,7 @@ namespace parser ast::ClassDefn* parseClass(State& st); ast::StaticDecl* parseStaticDecl(State& st); - ast::StructDefn* parseStruct(State& st, bool nameless); + PResult parseStruct(State& st, bool nameless); ast::UnionDefn* parseUnion(State& st, bool israw, bool nameless); ast::Expr* parseDollarExpr(State& st); diff --git a/source/include/repl.h b/source/include/repl.h index 55849b8c..d2d144dd 100644 --- a/source/include/repl.h +++ b/source/include/repl.h @@ -8,6 +8,12 @@ #include +namespace ztmu +{ + struct State; +} + + namespace repl { struct State; @@ -18,12 +24,15 @@ namespace repl bool processLine(const std::string& line); std::optional parseAndTypecheck(const std::string& line, bool* needmore); - void runCommand(const std::string& command); + bool runCommand(const std::string& command, ztmu::State* consoleState); // used to save/restore if we wanna do weird things. void setEnvironment(State* st); State* getEnvironment(); + void saveHistory(const std::vector>& history); + std::vector> loadHistory(); + template static void error(const std::string& fmt, Args&&... args) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 0cf4b774..d6cc4a81 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -1447,17 +1447,19 @@ namespace detail bool didSetSignalHandler = false; // time for some signalling! - struct sigaction new_sa { - .sa_flags = SA_RESTART, // this is important, if not read() will return EINTR (interrupted by signal) - .sa_handler = [](int sig) { - if(currentStateForSignal) - { - currentStateForSignal->termWidth = getTerminalWidth(); - currentStateForSignal->termHeight = getTerminalHeight(); - } + struct sigaction new_sa; + + // i would use designated initialisers, but some compilers refuse to cooperate. + new_sa.sa_flags = SA_RESTART; // this is important, if not read() will return EINTR (interrupted by signal) + new_sa.sa_handler = [](int sig) { + if(currentStateForSignal) + { + currentStateForSignal->termWidth = getTerminalWidth(); + currentStateForSignal->termHeight = getTerminalHeight(); } }; + struct sigaction old_sa; if(sigaction(SIGWINCH, &new_sa, &old_sa) == 0) didSetSignalHandler = true; @@ -1946,6 +1948,7 @@ namespace ztmu inline void State::loadHistory(const std::vector>& h) { + this->historyIdx = 0; this->history = h; } diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index 3fb7231d..ed17ec81 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -53,21 +53,22 @@ namespace platform std::string getEnvironmentVar(const std::string& name) { + #if OS_WINDOWS char buffer[256] = { 0 }; size_t len = 0; - #if OS_WINDOWS if(getenv_s(&len, buffer, name.c_str()) != 0) - #else - if(getenv(name.c_str()) == 0) - #endif - { return ""; - } + else - { return std::string(buffer, len); - } + #else + if(char* val = getenv(name.c_str()); val) + return std::string(val); + + else + return ""; + #endif } void pushEnvironmentVar(const std::string& name, const std::string& value) diff --git a/source/repl/commands.cpp b/source/repl/commands.cpp index 6974971c..dca49d6d 100644 --- a/source/repl/commands.cpp +++ b/source/repl/commands.cpp @@ -16,13 +16,12 @@ namespace repl static void print_help(); static void print_type(const std::string& expr); - void runCommand(const std::string& s) + bool runCommand(const std::string& s, ztmu::State* consoleState) { if(s == "q") { repl::log("exiting repl"); - - exit(0); + return true; } else if(s == "reset") { @@ -37,10 +36,17 @@ namespace repl { print_type(s.substr(2)); } + else if(s.find("clear_history") == 0) + { + // just loading an empty history will effectively clear the history. + consoleState->loadHistory({ }); + } else { repl::error("invalid command '%s'", s); } + + return false; } @@ -92,10 +98,11 @@ namespace repl zpr::sprint(""), zpr::sprint("%s*%s commands %s*%s", COLOUR_GREEN_BOLD, COLOUR_RESET, COLOUR_GREEN_BOLD, COLOUR_RESET), zpr::sprint("\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e\u203e"), - zpr::sprint(" :? / :help - display help (this listing)"), - zpr::sprint(" :q - quit the repl"), - zpr::sprint(" :reset - reset the environment, discarding all existing definitions"), - zpr::sprint(" :t - display the type of an expression"), + zpr::sprint(" :? / :help - display help (this listing)"), + zpr::sprint(" :q - quit the repl"), + zpr::sprint(" :reset - reset the environment, discarding all existing definitions"), + zpr::sprint(" :clear_history - clear the history of things"), + zpr::sprint(" :t - display the type of an expression"), }; static void print_help() diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index bad472c5..28bcd127 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -35,7 +35,8 @@ namespace repl // temporary. st.enableExitOnEmptyControlC(); - + // load the history. + st.loadHistory(repl::loadHistory()); // we need to put this up here, so the handler can capture it. int indentLevel = 0; @@ -61,7 +62,8 @@ namespace repl if(input[0] == ':') { - repl::runCommand(input.substr(1)); + auto quit = repl::runCommand(input.substr(1), &st); + if(quit) break; } else if(bool needmore = repl::processLine(input); needmore) { @@ -116,6 +118,9 @@ namespace repl // add an extra line printf("\n"); } + + // save the history. + repl::saveHistory(st.getHistory()); } } diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index ba0ceb0e..ac7daa3f 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -37,10 +37,15 @@ namespace repl this->fs = new sst::TypecheckState(tree); this->cs = new cgn::CodegenState(fir::IRBuilder(this->module)); this->cs->module = this->module; + + this->interpState = new fir::interp::InterpState(this->module); + this->interpState->initialise(/* runGlobalInit: */ true); // so we don't crash, give us a starting location. this->cs->pushLoc(Location()); + + fprintf(stderr, "NEW STATE!!!\n"); } ~State() @@ -150,14 +155,19 @@ namespace repl if(!stmt) return needmore; + { // copy some stuff over. state->cs->typeDefnMap = state->fs->typeDefnMap; - // ok, we have a thing. try to run it. + state->interpState->initialise(/* runGlobalInit: */ false); + // ok, we have a thing. try to run it. auto value = magicallyRunExpressionAtCompileTime(state->cs, *stmt, nullptr, - Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name)); + Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name), + state->interpState); + + state->interpState->finalise(); if(value) { @@ -173,7 +183,6 @@ namespace repl state->fs->stree->addDefinition(vardef->id.name, vardef); - printf("%s\n", zpr::sprint("%s: %s = %s", vardef->id.name, value->getType(), value->str()).c_str()); } } diff --git a/source/repl/history.cpp b/source/repl/history.cpp new file mode 100644 index 00000000..5e7f9439 --- /dev/null +++ b/source/repl/history.cpp @@ -0,0 +1,100 @@ +// history.cpp +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +#include +#include + +#include + +#include "repl.h" +#include "platform.h" + +namespace repl +{ + static std::string getConfigPath() + { + auto home = platform::getEnvironmentVar("HOME"); + + // do some checks so we don't try to write stuff into the root directory. + return (home + (home.empty() || home.back() == '/' ? "" : "/")) + ".flax-repl-history"; + } + + + void saveHistory(const std::vector>& history) + { + auto path = getConfigPath(); + auto file = std::ofstream(path, std::ios::out | std::ios::binary | std::ios::trunc); + if(!file.is_open() || !file.good()) + { + repl::log("failed to open file to load history (tried '%s')", path); + + char buf[128] = { 0 }; + strerror_r(errno, buf, 127); + repl::log("error was: '%s'", buf); + return; + } + + + // the format is that each "entry" is NULL-terminated, while each line in each entry is just + // newline-terminated. + + for(const auto& lines : history) + { + for(const auto& line : lines) + { + file.write(line.c_str(), line.size()); + file.put('\n'); + } + + // write the null-terminator + file.put('\0'); + } + + // ok. + file.close(); + } + + std::vector> loadHistory() + { + auto path = getConfigPath(); + auto file = std::ifstream(path, std::ios::in | std::ios::binary); + if(!file.is_open() || !file.good()) + return { }; + + std::vector> history; + + while(file.good()) + { + std::vector current; + for(std::string line; std::getline(file, line, '\n'); ) + { + current.push_back(line); + if(file.peek() == '\0') + { + file.get(); + break; + } + } + + if(!current.empty()) + history.push_back(current); + } + + + file.close(); + return history; + } +} + + + + + + + + + + + + From 815fde0bfe740de2a1147a40a34a41460e639877 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 21:41:31 +0800 Subject: [PATCH 076/129] Create continuous_integration.yml --- .github/workflows/continuous_integration.yml | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/continuous_integration.yml diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml new file mode 100644 index 00000000..0b9ba12d --- /dev/null +++ b/.github/workflows/continuous_integration.yml @@ -0,0 +1,34 @@ +name: CI +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - name: install dependencies + run: | + sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list + sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt -y update + sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 + - name: build + run: CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-9 make -j2 build + - name: jit/llvm + run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx + - name: jit/interp + run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx + - name: compile/llvm + run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester + + + + + + + + + + + + From 0dc0155f6dc64c3b397a5aae12b25469942a6fa7 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 21:44:03 +0800 Subject: [PATCH 077/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 0b9ba12d..99b7a596 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -13,7 +13,9 @@ jobs: sudo apt -y update sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 - name: build - run: CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-9 make -j2 build + env: + LLVM_CONFIG: llvm-config-9 + run: make -j2 build - name: jit/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - name: jit/interp From 381ed17f2dc47b041791c252dc32349ae346fd2c Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 21:58:06 +0800 Subject: [PATCH 078/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 37 ++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 99b7a596..8884fa2b 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -2,7 +2,7 @@ name: CI on: [push, pull_request] jobs: - build: + build-linux: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v1 @@ -11,10 +11,12 @@ jobs: sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt -y update - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 + sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y gcc-8 llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 - name: build env: LLVM_CONFIG: llvm-config-9 + CC: gcc-8 + CXX: g++-8 run: make -j2 build - name: jit/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx @@ -23,10 +25,33 @@ jobs: - name: compile/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester - - - - + build-macos: + runs-on: macOS-10.14 + steps: + - uses: actions/checkout@v1 + - name: cache llvm download + id: cache-llvm + uses: actions/cache@v1.0.0 + with: + path: llvm + key: ${{runner.os}}-llvm-9.0.0 + + - name: download llvm + if: steps.cache-llvm.outputs.cache-hit != 'true' + run: | + curl 'https://homebrew.bintray.com/bottles/llvm-9.0.0.high_sierra.bottle.tar.gz' -L -o llvm-9.0.0.tar.gz + tar -xf llvm-9.0.0.tar.gz + - name: build + env: + LLVM_CONFIG: LLVM_CONFIG=llvm-config + run: PATH="$PATH:$(pwd)/llvm/9.0.0/bin" make -j2 build + - name: jit/llvm + run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx + - name: jit/interp + run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx + - name: compile/llvm + run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester + From bf89d17f762d6dad831f28b51079e87a64638f0d Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:01:22 +0800 Subject: [PATCH 079/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 8884fa2b..c5f15ae6 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -11,12 +11,12 @@ jobs: sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt -y update - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y gcc-8 llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 + sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y gcc-9 llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 - name: build env: LLVM_CONFIG: llvm-config-9 - CC: gcc-8 - CXX: g++-8 + CC: gcc-9 + CXX: g++-9 run: make -j2 build - name: jit/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx From ca4acc909a94c8ce8394b033753abd5a76f755e7 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:08:56 +0800 Subject: [PATCH 080/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index c5f15ae6..793573e0 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -29,21 +29,12 @@ jobs: runs-on: macOS-10.14 steps: - uses: actions/checkout@v1 - - name: cache llvm download - id: cache-llvm - uses: actions/cache@v1.0.0 - with: - path: llvm - key: ${{runner.os}}-llvm-9.0.0 - - - name: download llvm - if: steps.cache-llvm.outputs.cache-hit != 'true' + - name: install dependencies run: | - curl 'https://homebrew.bintray.com/bottles/llvm-9.0.0.high_sierra.bottle.tar.gz' -L -o llvm-9.0.0.tar.gz - tar -xf llvm-9.0.0.tar.gz + brew install mpfr llvm pkg-config - name: build env: - LLVM_CONFIG: LLVM_CONFIG=llvm-config + LLVM_CONFIG: llvm-config run: PATH="$PATH:$(pwd)/llvm/9.0.0/bin" make -j2 build - name: jit/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx From 6d8f2c47888333c359fbf3fc4cb36761169f1173 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:09:10 +0800 Subject: [PATCH 081/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 793573e0..7a46cf76 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -11,7 +11,7 @@ jobs: sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt -y update - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y gcc-9 llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 + sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y g++-9 llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 - name: build env: LLVM_CONFIG: llvm-config-9 From a0e58dcc326eba41bf808bbb198a61eeb9ec0b9a Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:12:13 +0800 Subject: [PATCH 082/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 7a46cf76..115d3128 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -34,7 +34,7 @@ jobs: brew install mpfr llvm pkg-config - name: build env: - LLVM_CONFIG: llvm-config + LLVM_CONFIG: /usr/local/opt/llvm/bin/llvm-config run: PATH="$PATH:$(pwd)/llvm/9.0.0/bin" make -j2 build - name: jit/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx From 4d8e8d6a379ecfac609810043e4d9fd118b6da4d Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:34:44 +0800 Subject: [PATCH 083/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 33 ++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 115d3128..759de5f5 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -43,8 +43,37 @@ jobs: - name: compile/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester - - + build-windows: + runs-on: windows-2019 + env: + MPIR_ROOT_DIR: $HOME/lib/mpir + MPFR_ROOT_DIR: $HOME/lib/mpfr + LLVM_ROOT_DIR: $HOME/lib/llvm + LIBFFI_ROOT_DIR: $HOME/lib/libffi + steps: + - uses: seanmiddleditch/gha-setup-ninja@v1 + - name: cache libraries + uses: actions/cache@v1.0.0 + id: cache-libs + with: + path: $HOME/lib + key: cached-libs + + - name: download libraries + if: steps.cache-libs.outputs.cache-hit != 'true' + run: | + [Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls, Tls11, Tls12' + Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'libs.zip' + 7z x -y -o$Env:HOME\lib libs.zip + + - name: install meson + run: pip install meson + + - name: build + run: | + meson --buildtype=release build/meson-rel + ninja -C build/meson-rel + From 009d350325fecd45cb78c6f9198053440e983d2c Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:36:16 +0800 Subject: [PATCH 084/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 759de5f5..7108d202 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -62,7 +62,7 @@ jobs: - name: download libraries if: steps.cache-libs.outputs.cache-hit != 'true' run: | - [Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls, Tls11, Tls12' + [Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls' Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'libs.zip' 7z x -y -o$Env:HOME\lib libs.zip From e383d0bdf6ab6f870272189f2bc2c4bf2ccc14af Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:37:29 +0800 Subject: [PATCH 085/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 7108d202..5b48cfc4 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -62,7 +62,6 @@ jobs: - name: download libraries if: steps.cache-libs.outputs.cache-hit != 'true' run: | - [Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls' Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'libs.zip' 7z x -y -o$Env:HOME\lib libs.zip From 94a1f0cfda4af001efc7b98e0127d4dc1e03b728 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:41:35 +0800 Subject: [PATCH 086/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 5b48cfc4..45bc9c33 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -62,8 +62,9 @@ jobs: - name: download libraries if: steps.cache-libs.outputs.cache-hit != 'true' run: | + echo "root dirs:" $LLVM_ROOT_DIR Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'libs.zip' - 7z x -y -o$Env:HOME\lib libs.zip + 7z x -y -o"$HOME\lib" libs.zip - name: install meson run: pip install meson From 9a04ad66594df3211eb2986bf4b65d2affc1726e Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:46:49 +0800 Subject: [PATCH 087/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 45bc9c33..e77d0e0c 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -46,10 +46,10 @@ jobs: build-windows: runs-on: windows-2019 env: - MPIR_ROOT_DIR: $HOME/lib/mpir - MPFR_ROOT_DIR: $HOME/lib/mpfr - LLVM_ROOT_DIR: $HOME/lib/llvm - LIBFFI_ROOT_DIR: $HOME/lib/libffi + MPIR_ROOT_DIR: ${{env.HOME}}/lib/mpir + MPFR_ROOT_DIR: ${{env.HOME}}/lib/mpfr + LLVM_ROOT_DIR: ${{env.HOME}}/lib/llvm + LIBFFI_ROOT_DIR: ${{env.HOME}}/lib/libffi steps: - uses: seanmiddleditch/gha-setup-ninja@v1 - name: cache libraries @@ -68,11 +68,10 @@ jobs: - name: install meson run: pip install meson - + - name: build-1 + run: meson --buildtype=release build/meson-rel - name: build - run: | - meson --buildtype=release build/meson-rel - ninja -C build/meson-rel + run: ninja -C build/meson-rel From b409f86fe5f7a7a4de9a282426f5c8fa9ee0db01 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:52:55 +0800 Subject: [PATCH 088/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index e77d0e0c..ef87e503 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -46,10 +46,10 @@ jobs: build-windows: runs-on: windows-2019 env: - MPIR_ROOT_DIR: ${{env.HOME}}/lib/mpir - MPFR_ROOT_DIR: ${{env.HOME}}/lib/mpfr - LLVM_ROOT_DIR: ${{env.HOME}}/lib/llvm - LIBFFI_ROOT_DIR: ${{env.HOME}}/lib/libffi + MPIR_ROOT_DIR: C:/tmp/lib/mpir + MPFR_ROOT_DIR: C:/tmp/lib/mpfr + LLVM_ROOT_DIR: C:/tmp/lib/llvm + LIBFFI_ROOT_DIR: C:/tmp/lib/libffi steps: - uses: seanmiddleditch/gha-setup-ninja@v1 - name: cache libraries @@ -63,8 +63,9 @@ jobs: if: steps.cache-libs.outputs.cache-hit != 'true' run: | echo "root dirs:" $LLVM_ROOT_DIR + echo "extraction: C:\tmp\lib" Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'libs.zip' - 7z x -y -o"$HOME\lib" libs.zip + 7z x -y -o"C:\tmp\lib" libs.zip - name: install meson run: pip install meson From 015f784b49628e5c13b016f6597fa380eb85438e Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 22:56:08 +0800 Subject: [PATCH 089/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index ef87e503..f10d3ef9 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -52,13 +52,13 @@ jobs: LIBFFI_ROOT_DIR: C:/tmp/lib/libffi steps: - uses: seanmiddleditch/gha-setup-ninja@v1 + - uses: actions/checkout@v1 - name: cache libraries uses: actions/cache@v1.0.0 id: cache-libs with: path: $HOME/lib key: cached-libs - - name: download libraries if: steps.cache-libs.outputs.cache-hit != 'true' run: | @@ -66,13 +66,12 @@ jobs: echo "extraction: C:\tmp\lib" Invoke-WebRequest 'https://github.com/flax-lang/flax/releases/download/win-build-deps/libraries.zip' -OutFile 'libs.zip' 7z x -y -o"C:\tmp\lib" libs.zip - - name: install meson run: pip install meson - - name: build-1 - run: meson --buildtype=release build/meson-rel - name: build - run: ninja -C build/meson-rel + run: | + meson --buildtype=release build/meson-rel + ninja -C build/meson-rel From f3ef3c89881e552087251aeacefddcf41fc56354 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 23:01:05 +0800 Subject: [PATCH 090/129] Update continuous_integration.yml --- .github/workflows/continuous_integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index f10d3ef9..2f4f4860 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -52,6 +52,7 @@ jobs: LIBFFI_ROOT_DIR: C:/tmp/lib/libffi steps: - uses: seanmiddleditch/gha-setup-ninja@v1 + - uses: seanmiddleditch/gha-setup-vsdevenv@master - uses: actions/checkout@v1 - name: cache libraries uses: actions/cache@v1.0.0 From 40ec6edc3ee08e11c47ef9d51c2c8ff8d18c14c3 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 23:10:06 +0800 Subject: [PATCH 091/129] final update for gha!!! (hopefully???) --- .github/workflows/continuous_integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 2f4f4860..6a0ca3e0 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -58,7 +58,7 @@ jobs: uses: actions/cache@v1.0.0 id: cache-libs with: - path: $HOME/lib + path: C:/tmp/lib key: cached-libs - name: download libraries if: steps.cache-libs.outputs.cache-hit != 'true' From 3fc9f92641b85a5b6b23d28106c89ad252f13aac Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 23:19:32 +0800 Subject: [PATCH 092/129] oops i forgot to run any tests. --- .github/workflows/continuous_integration.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 6a0ca3e0..0888bc3e 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -73,6 +73,14 @@ jobs: run: | meson --buildtype=release build/meson-rel ninja -C build/meson-rel + - name: jit/llvm + run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend llvm build\tester.flx + - name: jit/interp + run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend interp build\tester.flx + - name: compile/llvm + run: | + build\meson-rel\flaxc.exe -sysroot build\sysroot build\tester.flx + .\tester.exe From 27ab132af8722333675f4feb840d96fc66effd85 Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 23:35:34 +0800 Subject: [PATCH 093/129] oops #2 --- .github/workflows/continuous_integration.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 0888bc3e..d990a68e 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -73,6 +73,10 @@ jobs: run: | meson --buildtype=release build/meson-rel ninja -C build/meson-rel + - name: copy stdlib + run: | + New-Item -Force -Path build\sysroot\usr\local\lib\flaxlibs -ItemType Directory + Copy-Item -Recurse -Force libs\* build\sysroot\usr\local\lib\flaxlibs\ - name: jit/llvm run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend llvm build\tester.flx - name: jit/interp From ab8ec0cbf2dcbad772bf3339380057a26ad939cd Mon Sep 17 00:00:00 2001 From: zhiayang <500236+zhiayang@users.noreply.github.com> Date: Sat, 16 Nov 2019 23:45:09 +0800 Subject: [PATCH 094/129] last one i promise --- .github/workflows/continuous_integration.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index d990a68e..ced5de46 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -78,12 +78,12 @@ jobs: New-Item -Force -Path build\sysroot\usr\local\lib\flaxlibs -ItemType Directory Copy-Item -Recurse -Force libs\* build\sysroot\usr\local\lib\flaxlibs\ - name: jit/llvm - run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend llvm build\tester.flx + run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend llvm --ffi-escape build\tester.flx - name: jit/interp - run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend interp build\tester.flx + run: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend interp --ffi-escape build\tester.flx - name: compile/llvm run: | - build\meson-rel\flaxc.exe -sysroot build\sysroot build\tester.flx + build\meson-rel\flaxc.exe -sysroot build\sysroot --ffi-escape build\tester.flx .\tester.exe From 70279e4fbdba0dd72daa4ea04f89db483fdd21aa Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 17 Nov 2019 00:03:36 +0800 Subject: [PATCH 095/129] fix issue regarding global inits in the repl also other stuff probably. forgot. --- README.md | 8 ++++---- appveyor.yml | 4 ++-- makefile | 2 +- source/codegen/directives.cpp | 32 +++++++++++++++++++------------ source/fir/interp/interpreter.cpp | 15 ++++++++++----- source/include/ztmu.h | 24 +++++++++++++++++++++++ source/repl/execute.cpp | 21 +++++++++++++++----- 7 files changed, 77 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 5accda4d..b88055fe 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ A low level, general-purpose language with high level syntax and expressibility. [![forthebadge](https://forthebadge.com/images/badges/made-with-crayons.svg)](http://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](http://forthebadge.com) -[![Build Status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) +[![build status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax)      -[![Build status](https://ci.appveyor.com/api/projects/status/c9cmm08t27ef1hji/branch/develop?svg=true)](https://ci.appveyor.com/project/zhiayang/flax/branch/develop) +[![build status](https://ci.appveyor.com/api/projects/status/c9cmm08t27ef1hji/branch/develop?svg=true)](https://ci.appveyor.com/project/zhiayang/flax/branch/develop)      -[![Build status](https://zhiayang.semaphoreci.com/badges/flax.svg)](zhiayang.semaphoreci.com/projects/flax) +[![build status](https://github.com/flax-lang/flax/workflows/CI/badge.svg)](https://github.com/flax-lang/flax/actions)      [![codecov](https://codecov.io/gh/flax-lang/flax/branch/develop/graph/badge.svg)](https://codecov.io/gh/flax-lang/flax)      -[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) +[![language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) Currently not expected to pass (for a while):
[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) diff --git a/appveyor.yml b/appveyor.yml index aaf3e580..574f7ffb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,9 +40,9 @@ test_script: - ps: cd c:\projects\flax - ps: New-Item -Force -Path build\sysroot\usr\local\lib\flaxlibs -ItemType Directory - ps: Copy-Item -Recurse -Force libs\* build\sysroot\usr\local\lib\flaxlibs\ - - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot -run -backend llvm build\tester.flx + - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot --ffi-escape -run -backend llvm build\tester.flx - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot --ffi-escape -run -backend interp build\tester.flx - - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot build\tester.flx + - ps: build\meson-rel\flaxc.exe -sysroot build\sysroot --ffi-escape build\tester.flx - ps: .\tester.exe after_test: diff --git a/makefile b/makefile index 7f458ea4..374f42cb 100644 --- a/makefile +++ b/makefile @@ -161,7 +161,7 @@ $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) $(UTF8REWIND_AR): - @make -C external/utf8rewind all + @$(MAKE) -C external/utf8rewind all # haha clena: clean diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp index bdfdf034..61f325ea 100644 --- a/source/codegen/directives.cpp +++ b/source/codegen/directives.cpp @@ -64,29 +64,37 @@ fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, s // run the function: fir::ConstantValue* ret = 0; { - bool needToFinalise = false; + // this unique_ptr handles destructing the temporary interpState when we're done. + using unique_ptr_alias = std::unique_ptr>; + auto ptr = unique_ptr_alias(); if(!is) { is = new fir::interp::InterpState(cs->module); is->initialise(/* runGlobalInit: */ true); - - needToFinalise = true; + ptr = unique_ptr_alias(is, [](fir::interp::InterpState* is) { + is->finalise(); + delete is; + }); } - + else { - auto result = is->runFunction(is->compileFunction(fn), { }); + // new strategy: run the initialisers anyway. + is->initialise(/* runGlobalInit: */ true); - if(!retty->isVoidType()) - ret = is->unwrapInterpValueIntoConstant(result); + // caller code will finalise. } - if(needToFinalise) - { - is->finalise(); - delete is; - } + auto result = is->runFunction(is->compileFunction(fn), { }); + + if(!retty->isVoidType()) + ret = is->unwrapInterpValueIntoConstant(result); } + // please get rid of the runner function + cs->module->removeFunction(fn); + delete fn; + + return ret; } diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 1c86e9b4..2d07466f 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -439,8 +439,6 @@ namespace interp InterpState::~InterpState() { - for(void* p : this->globalAllocs) - delete[] p; } void InterpState::initialise(bool runGlobalInit) @@ -478,7 +476,7 @@ namespace interp setValueRaw(this, &ret, &buffer, sizeof(void*)); ret.globalValTracker = glob; - this->globals[glob] = { ret, true }; + this->globals[glob] = { ret, false }; } for(const auto& [ id, intr ] : this->module->_getIntrinsicFunctions()) @@ -519,7 +517,7 @@ namespace interp } #endif - printf("module: %s\n", this->module->print().c_str()); + // printf("module:\n%s\n", this->module->print().c_str()); // truth be told it'll be more efficient to only get the function after checking runGlobalInit, but... meh. if(auto gif = this->module->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION)); @@ -552,11 +550,18 @@ namespace interp auto val = loadFromPtr(it->second.first, it->first->getType()); auto x = this->unwrapInterpValueIntoConstant(val); - printf("write-back: %s = %s\n", id.name.c_str(), x->str().c_str()); + // printf("write-back: %s = %s\n", id.name.c_str(), x->str().c_str()); glob->setInitialValue(x); + + it->second.second = false; } } } + + for(void* p : this->globalAllocs) + delete[] p; + + this->globalAllocs.clear(); } diff --git a/source/include/ztmu.h b/source/include/ztmu.h index d6cc4a81..85baff6a 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -1002,6 +1002,11 @@ namespace detail return down; } + inline void touch_line(State* st) + { + // when we touch the line, we reset the history index. + st->historyIdx = 0; + } inline void cursor_home(State* st) { @@ -1009,6 +1014,7 @@ namespace detail st->byteCursor = 0; refresh_line(st); + touch_line(st); } inline void cursor_end(State* st, bool refresh) @@ -1018,6 +1024,8 @@ namespace detail if(refresh) refresh_line(st); + + touch_line(st); } @@ -1025,6 +1033,7 @@ namespace detail // versions that handle lines. inline void delete_left(State* st) { + touch_line(st); if(st->cursor > 0 && getCurLine(st).size() > 0) { auto x = st->byteCursor - 1; @@ -1071,6 +1080,7 @@ namespace detail inline void delete_right(State* st) { + touch_line(st); if(st->byteCursor < getCurLine(st).size()) { auto x = st->byteCursor; @@ -1107,6 +1117,7 @@ namespace detail // bound to C-k by default static void delete_line_right(State* st) { + touch_line(st); if(st->byteCursor < st->lines[st->lineIdx].size()) { st->lines[st->lineIdx].erase(st->byteCursor); @@ -1130,6 +1141,7 @@ namespace detail static void _cursor_left(State* st, int n, bool refresh = true) { + touch_line(st); for(int i = 0; i < n; i++) { if(st->cursor > 0) @@ -1147,6 +1159,7 @@ namespace detail inline void cursor_left(State* st, bool refresh) { + touch_line(st); if(st->cursor > 0) { _cursor_left(st, 1, refresh); @@ -1171,6 +1184,7 @@ namespace detail // same deal with _cursor_right as for _cursor_left. static void _cursor_right(State* st, int n, bool refresh = true) { + touch_line(st); for(int i = 0; i < n; i++) { if(st->byteCursor < getCurLine(st).size()) @@ -1189,6 +1203,7 @@ namespace detail inline void cursor_right(State* st, bool refresh) { + touch_line(st); if(st->byteCursor < getCurLine(st).size()) { _cursor_right(st, 1, refresh); @@ -1244,6 +1259,7 @@ namespace detail { if(st->cursor > 0 && st->wrappedLineIdx > 0) { + touch_line(st); size_t promptL = st->lineIdx == 0 ? st->normPL : st->contPL; // first, get the current horz cursor position: @@ -1276,6 +1292,8 @@ namespace detail } else if(st->lineIdx > 0) { + touch_line(st); + // ok -- we are now into weird strange territory. move up into the previous continuation line... ztmu_dbg("going up...\n"); @@ -1338,6 +1356,7 @@ namespace detail { if(st->byteCursor < getCurLine(st).size() && (st->lines.size() == 1 || st->wrappedLineIdx + 1 < st->cachedNWLForCurrentLine)) { + touch_line(st); // works on a similar principle as cursor_up. auto hcursor = get_cursor_line_offset(st->termWidth, st->cursor, @@ -1355,6 +1374,8 @@ namespace detail } else if(st->lineIdx + 1 < st->lines.size()) { + touch_line(st); + ztmu_dbg("going down...\n"); std::string_view nextLine = st->lines[st->lineIdx + 1]; @@ -1805,6 +1826,9 @@ namespace ztmu this->cachedNWLForCurrentLine = 0; this->wrappedLineIdx = 0; + + // we call clear on commit, so the history index needs to go back to 0. + this->historyIdx = 0; } inline void State::setPrompt(const std::string& prompt) diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index ac7daa3f..380ce5cf 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -44,8 +44,6 @@ namespace repl // so we don't crash, give us a starting location. this->cs->pushLoc(Location()); - - fprintf(stderr, "NEW STATE!!!\n"); } ~State() @@ -160,9 +158,22 @@ namespace repl // copy some stuff over. state->cs->typeDefnMap = state->fs->typeDefnMap; - state->interpState->initialise(/* runGlobalInit: */ false); + // so the thing is, all the previous things have already been code-generated, + // and have had their initialisers run. so there's really no need for their + // init pieces to stick around. we need to remove the functions as well for this + // to work properly! + for(auto [ gv, pc ] : state->cs->globalInitPieces) + { + state->module->removeFunction(pc); + delete pc; + } + + state->cs->globalInitPieces.clear(); - // ok, we have a thing. try to run it. + // ok, we have a thing. try to run it. note: this will help us to run is->initialise(true), + // which will call the global inits. this function also calls Stmt::codegen, which will + // (potentially) populate the globalInitPieces, before calling cs->finishGlobalInits(). basically, + // it's all handled. auto value = magicallyRunExpressionAtCompileTime(state->cs, *stmt, nullptr, Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name), state->interpState); @@ -183,7 +194,7 @@ namespace repl state->fs->stree->addDefinition(vardef->id.name, vardef); - printf("%s\n", zpr::sprint("%s: %s = %s", vardef->id.name, value->getType(), value->str()).c_str()); + zpr::println("%s: %s = %s", vardef->id.name, value->getType(), value->str()); } } From d065cff7165275515d29fe80ece061db90ab6fb3 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 17 Nov 2019 11:22:46 +0800 Subject: [PATCH 096/129] change FastInsertVector a bit, and fix more repl bugs. FastInsertVector now takes the chunk size as a template parameter instead of a runtime parameter. no idea why it was that way to begin with. --- source/fir/IRBuilder.cpp | 3 ++- source/fir/Instruction.cpp | 4 +--- source/include/container.h | 20 +++++++----------- source/include/defs.h | 37 ++++++++++++++++------------------ source/include/ir/value.h | 17 +++++++--------- source/include/memorypool.h | 2 +- source/include/ztmu.h | 3 +++ source/platform/msvcfinder.cpp | 2 +- 8 files changed, 39 insertions(+), 49 deletions(-) diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index e076a3f1..6de411d6 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -71,7 +71,8 @@ namespace fir return this->currentBlock; } - static util::MemoryPool instr_pool(65536); + static util::MemoryPool instr_pool; + static Instruction* make_instr(OpKind kind, bool sideEffects, Type* out, const std::vector& vals, Value::Kind k = Value::Kind::prvalue) { diff --git a/source/fir/Instruction.cpp b/source/fir/Instruction.cpp index 12b4639c..933ce9bb 100644 --- a/source/fir/Instruction.cpp +++ b/source/fir/Instruction.cpp @@ -11,9 +11,7 @@ namespace fir { - static util::MemoryPool value_pool(65536); - - + static util::MemoryPool value_pool; Instruction::Instruction(OpKind kind, bool sideeff, Type* out, const std::vector& vals) : Instruction(kind, sideeff, out, vals, Value::Kind::prvalue) { } diff --git a/source/include/container.h b/source/include/container.h index 2119684d..991a4ccf 100644 --- a/source/include/container.h +++ b/source/include/container.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -16,19 +17,16 @@ namespace util { // only allows for insertions. // hopefully faster than FastVector. - template + template struct FastInsertVector { - FastInsertVector(size_t cs = 256) + FastInsertVector() { this->length = 0; - this->ChunkSize = cs; } FastInsertVector(const FastInsertVector& other) { - this->ChunkSize = other.ChunkSize; - for(auto c : other.chunks) { auto nc = static_cast(mem::allocate_memory(sizeof(ValueType) * ChunkSize)); @@ -50,7 +48,6 @@ namespace util { // move. this->chunks = std::move(other.chunks); - this->ChunkSize = other.ChunkSize; this->length = other.length; other.length = 0; @@ -65,7 +62,6 @@ namespace util // move. this->chunks = std::move(other.chunks); - this->ChunkSize = other.ChunkSize; this->length = other.length; other.length = 0; @@ -85,7 +81,7 @@ namespace util size_t cind = index / ChunkSize; size_t offs = index % ChunkSize; - iceAssert(cind < this->chunks.size()); + assert(cind < this->chunks.size()); return *(static_cast(this->chunks[cind] + offs)); } @@ -130,8 +126,6 @@ namespace util // possibly use a faster implementation?? since we're just storing pointers idk if there's a point. size_t length; std::vector chunks; - - size_t ChunkSize; }; struct MemoryPool_base @@ -140,10 +134,10 @@ namespace util virtual ~MemoryPool_base() { } }; - template + template struct MemoryPool : MemoryPool_base { - MemoryPool(size_t chunkSize) : storage(chunkSize) { } + MemoryPool() { } ~MemoryPool() { this->storage.clear(); } MemoryPool(const MemoryPool& other) @@ -187,7 +181,7 @@ namespace util } private: - FastInsertVector storage; + FastInsertVector storage; }; } diff --git a/source/include/defs.h b/source/include/defs.h index 46f1e27d..35f1baeb 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -11,7 +11,7 @@ #include "zpr.h" #include "utils.h" - +#include "container.h" struct Identifier; enum class VisibilityLevel; @@ -245,9 +245,6 @@ enum class MsgType namespace util { - template struct MemoryPool; - template struct FastInsertVector; - struct ESpan { ESpan() { } @@ -291,8 +288,8 @@ struct ErrorMsg protected: ErrorMsg(ErrKind k, MsgType t) : kind(k), type(t) { } - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; }; struct BareError : ErrorMsg @@ -313,19 +310,19 @@ struct BareError : ErrorMsg BareError() : ErrorMsg(ErrKind::Bare, MsgType::Error) { } BareError(const std::string& m, MsgType t) : ErrorMsg(ErrKind::Bare, t), msg(m) { } - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; }; struct SimpleError : ErrorMsg { template - static SimpleError* make(const Location& l, const char* fmt, Ts&&... ts) { return util::make_SimpleError(l, strprintf(fmt, ts...)); } + static SimpleError* make(const Location& l, const char* fmt, Ts&&... ts) + { return util::make_SimpleError(l, strprintf(fmt, ts...)); } template - static SimpleError* make(MsgType t, const Location& l, const char* fmt, Ts&&... ts) { return util::make_SimpleError(l, strprintf(fmt, ts...), t); } - - + static SimpleError* make(MsgType t, const Location& l, const char* fmt, Ts&&... ts) + { return util::make_SimpleError(l, strprintf(fmt, ts...), t); } virtual void post() override; @@ -343,8 +340,8 @@ struct SimpleError : ErrorMsg SimpleError() : ErrorMsg(ErrKind::Simple, MsgType::Error) { } SimpleError(const Location& l, const std::string& m, MsgType t) : ErrorMsg(ErrKind::Bare, t), loc(l), msg(m) { } - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; }; struct ExampleMsg : ErrorMsg @@ -361,8 +358,8 @@ struct ExampleMsg : ErrorMsg ExampleMsg() : ErrorMsg(ErrKind::Example, MsgType::Note) { } ExampleMsg(const std::string& eg, MsgType t) : ErrorMsg(ErrKind::Example, t), example(eg) { } - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; }; @@ -391,8 +388,8 @@ struct SpanError : ErrorMsg SpanError(SimpleError* se, const std::vector& s, MsgType t) : ErrorMsg(ErrKind::Span, t), top(se), spans(s) { } - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; }; @@ -418,8 +415,8 @@ struct OverloadError : ErrorMsg OverloadError() : ErrorMsg(ErrKind::Overload, MsgType::Error) { } OverloadError(SimpleError* se, MsgType t) : ErrorMsg(ErrKind::Overload, t), top(se) { } - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; }; diff --git a/source/include/ir/value.h b/source/include/ir/value.h index b4a60123..ed26d8bb 100644 --- a/source/include/ir/value.h +++ b/source/include/ir/value.h @@ -8,14 +8,9 @@ #include #include -#include "errors.h" #include "type.h" - -namespace util -{ - template struct MemoryPool; - template struct FastInsertVector; -} +#include "errors.h" +#include "container.h" namespace fir { @@ -41,8 +36,8 @@ namespace fir friend struct Instruction; friend struct ConstantValue; - friend struct util::MemoryPool; - friend struct util::FastInsertVector; + template friend struct util::MemoryPool; + template friend struct util::FastInsertVector; // congratulations, i fucking played myself. enum class Kind @@ -74,8 +69,10 @@ namespace fir // protected shit size_t id; protected: - Value(Type* type, Kind k = Kind::prvalue); + virtual ~Value() { } + Value(Type* type, Kind k = Kind::prvalue); + // fields Identifier ident; diff --git a/source/include/memorypool.h b/source/include/memorypool.h index 4ae0bc90..b126bb16 100644 --- a/source/include/memorypool.h +++ b/source/include/memorypool.h @@ -15,7 +15,7 @@ namespace util template T* pool(Args&&... args) { - static MemoryPool _pool(512); + static MemoryPool _pool; addPool(&_pool); return _pool.construct(std::forward(args)...); } diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 85baff6a..9134106b 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -1541,6 +1541,9 @@ namespace detail eof = true; st->clear(); + + // to prevent fuckery, we need to add an empty line back in! + st->lines.push_back(""); goto finish_now; } } diff --git a/source/platform/msvcfinder.cpp b/source/platform/msvcfinder.cpp index b02e6be6..94b4b64b 100644 --- a/source/platform/msvcfinder.cpp +++ b/source/platform/msvcfinder.cpp @@ -21,7 +21,7 @@ namespace platform { namespace compiler { // the techniques used here are with reference to Jon Blow's "microsoft_craziness.h" file. - // it was released under the MIT license. + // it was released under the MIT license. see: https://gist.github.com/machinamentum/a2b587a68a49094257da0c39a6c4405f struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown { From e67bde39d99c6018d7d07a345a8bdfb8a7a3c1f6 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 17 Nov 2019 18:12:54 +0800 Subject: [PATCH 097/129] fix compile error on linux --- source/codegen/directives.cpp | 2 ++ source/typecheck/typecheckstate.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp index 61f325ea..6ba1a845 100644 --- a/source/codegen/directives.cpp +++ b/source/codegen/directives.cpp @@ -2,6 +2,8 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. +#include + #include "sst.h" #include "codegen.h" #include "platform.h" diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 5de0920b..f2057593 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -406,8 +406,8 @@ namespace sst return ret; } - bool TypecheckState::checkForShadowingOrConflictingDefinition(Defn* defn, std::function conflictCheckCallback, - StateTree* tree) + bool TypecheckState::checkForShadowingOrConflictingDefinition(Defn* defn, + std::function conflictCheckCallback, StateTree* tree) { if(tree == 0) tree = this->stree; From 9cfbd94c6c8af580cf258993e9620d8099694801 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 18 Nov 2019 17:15:16 +0800 Subject: [PATCH 098/129] fix repl and build on windows. --- .gitignore | 1 + meson.build | 6 +- source/include/ztmu.h | 347 ++++++++++++++++++++++++---------- source/platform/backtrace.cpp | 4 +- source/platform/platform.cpp | 2 +- source/repl/driver.cpp | 6 + source/repl/history.cpp | 4 + 7 files changed, 262 insertions(+), 108 deletions(-) diff --git a/.gitignore b/.gitignore index 7ccf0f5b..29a500b8 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,7 @@ build/cmake-output .idea # CMakeLists.txt +.flax-repl-history docs/.antlr diff --git a/meson.build b/meson.build index 767ffda4..765147b1 100644 --- a/meson.build +++ b/meson.build @@ -1,3 +1,5 @@ +# meson.build + project('flax', version: '0.41.7-pre', default_options: [ 'warning_level=3' ]) add_languages(['c', 'cpp']) @@ -39,7 +41,7 @@ if the_compiler.get_id() == 'msvc' add_project_link_arguments('/machine:X64', language: ['c', 'cpp']) add_project_link_arguments('/opt:noref', language: ['c', 'cpp']) - add_project_link_arguments('/incremental', language: ['c', 'cpp']) + # add_project_link_arguments('/incremental', language: ['c', 'cpp']) add_project_link_arguments('/nodefaultlib:libcmt.lib', language: ['c', 'cpp']) add_project_link_arguments('/nodefaultlib:libcmtd.lib', language: ['c', 'cpp']) @@ -188,6 +190,7 @@ source_files = files([ 'source/repl/driver.cpp', 'source/repl/execute.cpp', + 'source/repl/history.cpp', 'source/repl/commands.cpp', 'source/frontend/pts.cpp', @@ -201,6 +204,7 @@ source_files = files([ 'source/platform/compiler.cpp', 'source/platform/platform.cpp', + 'source/platform/backtrace.cpp', 'source/platform/msvcfinder.cpp', 'source/frontend/parser/misc.cpp', diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 9134106b..5423307b 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -51,6 +52,9 @@ namespace ztmu { void clear(); + void setupConsole(); + void unsetupConsole(); + std::optional read(); std::optional> readContinuation(const std::string& seed = ""); @@ -145,6 +149,9 @@ namespace detail int params[8]; }; + void setup_console(); + void unsetup_console(); + size_t getTerminalWidth(); size_t displayedTextLength(const std::string_view& str); bool read_line(State* st, int promptMode, std::string seed); @@ -162,7 +169,9 @@ namespace detail - +// close out the scope here -- we don't want to #include headers in a namespace. +} +} @@ -186,6 +195,10 @@ namespace detail #include #include + +namespace ztmu { +namespace detail +{ static struct termios original_termios; @@ -204,22 +217,213 @@ namespace detail return read(STDIN_FILENO, c, len); } + static bool isInRawMode = false; + static inline void leaveRawMode() + { + if(isInRawMode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios) != -1) + isInRawMode = false; + + // disable bracketed paste: + platform_write("\x1b[?2004l"); + } + + static inline void enterRawMode() + { + if(!isatty(STDIN_FILENO)) + return; + + if(!didRegisterAtexit) + { + atexit([]() { + leaveRawMode(); + }); + + didRegisterAtexit = true; + } + + if(tcgetattr(STDIN_FILENO, &original_termios) == -1) + return; + + // copy the original + termios raw = original_termios; + + // input modes: no break, no CR to NL, no parity check, no strip char, + // no start/stop output control. + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + + // output modes - disable post processing + raw.c_oflag &= ~(OPOST); + + // control modes - set 8 bit chars + raw.c_cflag |= (CS8); + + // local modes - echoing off, canonical off, no extended functions, + // no signal chars (^Z,^C) + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + + // control chars - set return condition: min number of bytes and timer. + // We want read to return every single byte, without timeout. + raw.c_cc[VMIN] = 1; + raw.c_cc[VTIME] = 0; + + /* put terminal in raw mode after flushing */ + if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) + return; + + // enable bracketed paste: + platform_write("\x1b[?2004h"); + isInRawMode = true; + } + + static inline void setup_console() + { + // on unix, we don't really need to do anything "globally". + } + + static inline void unsetup_console() + { + } + + static struct sigaction old_sigact; + static inline bool setup_sigwinch() + { + // time for some signalling! + struct sigaction new_sa; + + // i would use designated initialisers, but some compilers refuse to cooperate. + new_sa.sa_flags = SA_RESTART; // this is important, if not read() will return EINTR (interrupted by signal) + new_sa.sa_handler = [](int sig) { + if(currentStateForSignal) + { + currentStateForSignal->termWidth = getTerminalWidth(); + currentStateForSignal->termHeight = getTerminalHeight(); + } + }; + + + if(sigaction(SIGWINCH, &new_sa, &old_sigact) == 0) + return true; + + return false; + } + + static inline void restore_sigwinch() + { + sigaction(SIGWINCH, &old_sigact, nullptr); + } + + #else + // mfw + #define WIN32_LEAN_AND_MEAN 1 + + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #include + #include + #include + +namespace ztmu { +namespace detail +{ + static constexpr int STDOUT_FILENO = 1; + static constexpr int STDIN_FILENO = 0; static inline void platform_write(const char* s, size_t len) { - _write(STDOUT_FILENO, s, len); + DWORD written = 0; + WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), s, static_cast(len), &written, NULL); } static inline int platform_read_one(char* c) { - return _read(STDIN_FILENO, c, 1); + // return _read(STDIN_FILENO, c, 1); + DWORD didRead = 0; + ReadConsole(GetStdHandle(STD_INPUT_HANDLE), c, 1, &didRead, NULL); + return didRead; } static inline int platform_read(char* c, size_t len) { - return _read(STDIN_FILENO, c, len); + DWORD didRead = 0; + ReadConsole(GetStdHandle(STD_INPUT_HANDLE), c, static_cast(len), &didRead, NULL); + return didRead; + } + + static DWORD old_stdin_mode = 0; + static DWORD old_stdout_mode = 0; + + static bool isInRawMode = false; + static inline void leaveRawMode() + { + if(!isInRawMode) + return; + + auto stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + SetConsoleMode(stdin_handle, old_stdin_mode); + SetConsoleMode(stdout_handle, old_stdout_mode); + + isInRawMode = false; + } + + + static inline void enterRawMode() + { + if(!_isatty(STDIN_FILENO)) + return; + + isInRawMode = true; + + auto stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + GetConsoleMode(stdin_handle, &old_stdin_mode); + GetConsoleMode(stdout_handle, &old_stdout_mode); + + { + // this is pretty much the only flag we want. + DWORD stdin_mode = ENABLE_EXTENDED_FLAGS | ENABLE_VIRTUAL_TERMINAL_INPUT; + SetConsoleMode(stdin_handle, stdin_mode); + + + // then stdout: + DWORD stdout_mode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + SetConsoleMode(stdout_handle, stdout_mode); + } + } + + static UINT old_input_cp; + static UINT old_output_cp; + inline void setup_console() + { + old_input_cp = GetConsoleCP(); + old_output_cp = GetConsoleOutputCP(); + + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + } + + inline void unsetup_console() + { + SetConsoleCP(old_input_cp); + SetConsoleOutputCP(old_output_cp); + } + + static inline bool setup_sigwinch() + { + // on windows we can't really do anything. knowing about window size changes requires using + // ReadConsoleInput to poll for events, which we obviously can't do here, or use another + // thread, which i'm loathe to do. so for now, suck it up, windows users. + return false; + } + + static inline void restore_sigwinch() + { } #endif @@ -242,47 +446,47 @@ namespace detail + + + + + static constexpr const char ESC = '\x1b'; static constexpr const char* CSI = "\x1b["; static bool didRegisterAtexit = false; - static bool isInRawMode = false; static inline void platform_write(const std::string_view& sv) { platform_write(sv.data(), sv.size()); } - static inline std::string moveCursorUp(int n); - static inline std::string moveCursorDown(int n); - static inline std::string moveCursorLeft(int n); - static inline std::string moveCursorRight(int n); + static inline std::string moveCursorUp(size_t n); + static inline std::string moveCursorDown(size_t n); + static inline std::string moveCursorLeft(size_t n); + static inline std::string moveCursorRight(size_t n); - static inline std::string moveCursorUp(int n) + static inline std::string moveCursorUp(size_t n) { if(n == 0) return ""; - if(n < 0) return moveCursorDown(-n); else return zpr::sprint("%s%dA", CSI, n); } - static inline std::string moveCursorDown(int n) + static inline std::string moveCursorDown(size_t n) { if(n == 0) return ""; - if(n < 0) return moveCursorUp(-n); else return zpr::sprint("%s%dB", CSI, n); } - static inline std::string moveCursorLeft(int n) + static inline std::string moveCursorLeft(size_t n) { if(n == 0) return ""; - if(n < 0) return moveCursorRight(-n); else return zpr::sprint("%s%dD", CSI, n); } - static inline std::string moveCursorRight(int n) + static inline std::string moveCursorRight(size_t n) { if(n == 0) return ""; - if(n < 0) return moveCursorLeft(-n); else return zpr::sprint("%s%dC", CSI, n); } @@ -294,7 +498,7 @@ namespace detail char buf[33] { 0 }; for(size_t i = 0; i < 32; i++) { - if(read(STDIN_FILENO, buf + i, 1) != 1) + if(platform_read_one(buf + i) != 1) break; if(buf[i] == 'R') @@ -428,7 +632,7 @@ namespace detail return len; }; - auto len = utf8_len(str.begin(), str.end()); + auto len = utf8_len(str.data(), str.data() + str.size()); { size_t cons = 0; std::string_view sv = str; @@ -452,62 +656,6 @@ namespace detail } } - static void leaveRawMode() - { - if(isInRawMode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios) != -1) - isInRawMode = false; - - // disable bracketed paste: - platform_write("\x1b[?2004l"); - } - - static void enterRawMode() - { - if(!isatty(STDIN_FILENO)) - return; - - if(!didRegisterAtexit) - { - atexit([]() { - leaveRawMode(); - }); - - didRegisterAtexit = true; - } - - if(tcgetattr(STDIN_FILENO, &original_termios) == -1) - return; - - // copy the original - termios raw = original_termios; - - // input modes: no break, no CR to NL, no parity check, no strip char, - // no start/stop output control. - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - - // output modes - disable post processing - raw.c_oflag &= ~(OPOST); - - // control modes - set 8 bit chars - raw.c_cflag |= (CS8); - - // local modes - echoing off, canonical off, no extended functions, - // no signal chars (^Z,^C) - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - - // control chars - set return condition: min number of bytes and timer. - // We want read to return every single byte, without timeout. - raw.c_cc[VMIN] = 1; - raw.c_cc[VTIME] = 0; - - /* put terminal in raw mode after flushing */ - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) - return; - - // enable bracketed paste: - platform_write("\x1b[?2004h"); - isInRawMode = true; - } inline size_t getTerminalWidth() { @@ -604,7 +752,7 @@ namespace detail } - static std::pair calculate_left_codepoints(int n, const std::string_view& sv, size_t cursor, size_t byteCursor) + static std::pair calculate_left_codepoints(size_t n, const std::string_view& sv, size_t cursor, size_t byteCursor) { for(int i = 0; i < n; i++) { @@ -1139,10 +1287,10 @@ namespace detail // a single line ("logical line"), while the latter will move to the previous line if the current // line is exhausted. user-inputs call cursor_left(), while internal users should call _cursor_left(). - static void _cursor_left(State* st, int n, bool refresh = true) + static void _cursor_left(State* st, size_t n, bool refresh = true) { touch_line(st); - for(int i = 0; i < n; i++) + for(size_t i = 0; i < n; i++) { if(st->cursor > 0) { @@ -1182,10 +1330,10 @@ namespace detail // same deal with _cursor_right as for _cursor_left. - static void _cursor_right(State* st, int n, bool refresh = true) + static void _cursor_right(State* st, size_t n, bool refresh = true) { touch_line(st); - for(int i = 0; i < n; i++) + for(size_t i = 0; i < n; i++) { if(st->byteCursor < getCurLine(st).size()) { @@ -1465,25 +1613,7 @@ namespace detail platform_write(promptMode == 0 ? st->promptString : st->contPromptString); platform_write(seed); - bool didSetSignalHandler = false; - - // time for some signalling! - struct sigaction new_sa; - - // i would use designated initialisers, but some compilers refuse to cooperate. - new_sa.sa_flags = SA_RESTART; // this is important, if not read() will return EINTR (interrupted by signal) - new_sa.sa_handler = [](int sig) { - if(currentStateForSignal) - { - currentStateForSignal->termWidth = getTerminalWidth(); - currentStateForSignal->termHeight = getTerminalHeight(); - } - }; - - - struct sigaction old_sa; - if(sigaction(SIGWINCH, &new_sa, &old_sa) == 0) - didSetSignalHandler = true; + bool didSetSignalHandler = setup_sigwinch(); auto commit_line = [&](bool refresh = true) { @@ -1646,7 +1776,7 @@ namespace detail } else { - thing = n; + thing = static_cast(n); } if(n == 200) @@ -1793,7 +1923,7 @@ namespace detail // restore the signal state, and reset the terminal to normal mode. currentStateForSignal = 0; if(didSetSignalHandler) - sigaction(SIGWINCH, &old_sa, nullptr); + restore_sigwinch(); leaveRawMode(); @@ -1801,11 +1931,10 @@ namespace detail return eof; } - -#endif - } } +#endif + namespace ztmu @@ -1820,6 +1949,16 @@ namespace ztmu return detail::getTerminalWidth(); } + inline void State::setupConsole() + { + detail::setup_console(); + } + + inline void State::unsetupConsole() + { + detail::unsetup_console(); + } + inline void State::clear() { // reset the thing. diff --git a/source/platform/backtrace.cpp b/source/platform/backtrace.cpp index 83867361..cc855edd 100644 --- a/source/platform/backtrace.cpp +++ b/source/platform/backtrace.cpp @@ -30,7 +30,7 @@ namespace platform { size_t num; uintptr_t address; - ssize_t offset; + size_t offset; std::string modName; std::string mangledName; @@ -61,7 +61,7 @@ namespace platform char modname[1024] { }; char funcname[1024] { }; - sscanf(strs[i], "%*s %s %zx %s %*s %zd", &modname[0], &piece.address, &funcname[0], &piece.offset); + sscanf(strs[i], "%*s %s %zx %s %*s %zu", &modname[0], &piece.address, &funcname[0], &piece.offset); piece.mangledName = funcname; piece.modName = modname; diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index ed17ec81..d9739dd6 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -424,7 +424,7 @@ namespace platform } // then, change the codepage to utf-8: - SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); #else diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 28bcd127..32846ca2 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -38,6 +38,9 @@ namespace repl // load the history. st.loadHistory(repl::loadHistory()); + // setup the console (on windows, this changes to utf8 codepage.) + st.setupConsole(); + // we need to put this up here, so the handler can capture it. int indentLevel = 0; @@ -121,6 +124,9 @@ namespace repl // save the history. repl::saveHistory(st.getHistory()); + + // restore the console. + st.unsetupConsole(); } } diff --git a/source/repl/history.cpp b/source/repl/history.cpp index 5e7f9439..5bd6a4c1 100644 --- a/source/repl/history.cpp +++ b/source/repl/history.cpp @@ -30,7 +30,11 @@ namespace repl repl::log("failed to open file to load history (tried '%s')", path); char buf[128] = { 0 }; + #if OS_WINDOWS + strerror_s(buf, 127, errno); + #else strerror_r(errno, buf, 127); + #endif repl::log("error was: '%s'", buf); return; } From 5613a7d4fe3130d513781cafe65a28ed5cf0a2d4 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 18 Nov 2019 17:22:03 +0800 Subject: [PATCH 099/129] unbreak linux build --- source/include/ztmu.h | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index 5423307b..b2b3e243 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -153,6 +153,7 @@ namespace detail void unsetup_console(); size_t getTerminalWidth(); + size_t getTerminalHeight(); size_t displayedTextLength(const std::string_view& str); bool read_line(State* st, int promptMode, std::string seed); void cursor_left(State* st, bool refresh = true); @@ -218,13 +219,14 @@ namespace detail } static bool isInRawMode = false; + static bool didRegisterAtexit = false; static inline void leaveRawMode() { if(isInRawMode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios) != -1) isInRawMode = false; // disable bracketed paste: - platform_write("\x1b[?2004l"); + platform_write(std::string_view("\x1b[?2004l")); } static inline void enterRawMode() @@ -271,21 +273,21 @@ namespace detail return; // enable bracketed paste: - platform_write("\x1b[?2004h"); + platform_write(std::string_view("\x1b[?2004h")); isInRawMode = true; } - static inline void setup_console() + inline void setup_console() { // on unix, we don't really need to do anything "globally". } - static inline void unsetup_console() + inline void unsetup_console() { } static struct sigaction old_sigact; - static inline bool setup_sigwinch() + static inline bool setup_sigwinch(State* currentStateForSignal) { // time for some signalling! struct sigaction new_sa; @@ -357,6 +359,8 @@ namespace detail static DWORD old_stdout_mode = 0; static bool isInRawMode = false; + static bool didRegisterAtexit = false; + static inline void leaveRawMode() { if(!isInRawMode) @@ -395,6 +399,15 @@ namespace detail DWORD stdout_mode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; SetConsoleMode(stdout_handle, stdout_mode); } + + if(!didRegisterAtexit) + { + atexit([]() { + leaveRawMode(); + }); + + didRegisterAtexit = true; + } } static UINT old_input_cp; @@ -414,7 +427,7 @@ namespace detail SetConsoleOutputCP(old_output_cp); } - static inline bool setup_sigwinch() + static inline bool setup_sigwinch(State* currentStateForSignal) { // on windows we can't really do anything. knowing about window size changes requires using // ReadConsoleInput to poll for events, which we obviously can't do here, or use another @@ -446,16 +459,9 @@ namespace detail - - - - - static constexpr const char ESC = '\x1b'; static constexpr const char* CSI = "\x1b["; - static bool didRegisterAtexit = false; - static inline void platform_write(const std::string_view& sv) { platform_write(sv.data(), sv.size()); @@ -754,7 +760,7 @@ namespace detail static std::pair calculate_left_codepoints(size_t n, const std::string_view& sv, size_t cursor, size_t byteCursor) { - for(int i = 0; i < n; i++) + for(size_t i = 0; i < n; i++) { if(cursor > 0) { @@ -1111,7 +1117,7 @@ namespace detail // see if we need to scroll. note that _refresh_line will scroll for the current line -- ONLY IF IT WRAPS // but because we are drawing all lines, we need space for the next line as well. - if(getCursorPosition().y == st->termHeight) + if(getCursorPosition().y == static_cast(st->termHeight)) buffer += zpr::sprint("%s1S", CSI); // one should be enough. buffer += moveCursorDown(down); @@ -1613,7 +1619,7 @@ namespace detail platform_write(promptMode == 0 ? st->promptString : st->contPromptString); platform_write(seed); - bool didSetSignalHandler = setup_sigwinch(); + bool didSetSignalHandler = setup_sigwinch(currentStateForSignal); auto commit_line = [&](bool refresh = true) { @@ -1790,7 +1796,7 @@ namespace detail while(platform_read_one(&x) > 0) { pasted += x; - if(pasted.rfind("\x1b[201~") != -1) + if(pasted.rfind("\x1b[201~") != std::string::npos) break; } From 09fa84c2646bd6978af78ee5d98a3d3e833c067e Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 18 Nov 2019 17:27:00 +0800 Subject: [PATCH 100/129] try harder to fix linux build --- source/include/ztmu.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/source/include/ztmu.h b/source/include/ztmu.h index b2b3e243..ce7c9df0 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -223,10 +223,11 @@ namespace detail static inline void leaveRawMode() { if(isInRawMode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios) != -1) - isInRawMode = false; + isInRawMode = false; - // disable bracketed paste: - platform_write(std::string_view("\x1b[?2004l")); + // disable bracketed paste: + auto tmp = std::string_view("\x1b[?2004l"); + platform_write(tmp.data(), tmp.size()); } static inline void enterRawMode() @@ -273,7 +274,8 @@ namespace detail return; // enable bracketed paste: - platform_write(std::string_view("\x1b[?2004h")); + auto tmp = std::string_view("\x1b[?2004h"); + platform_write(tmp.data(), tmp.size()); isInRawMode = true; } @@ -287,7 +289,8 @@ namespace detail } static struct sigaction old_sigact; - static inline bool setup_sigwinch(State* currentStateForSignal) + static State* currentStateForSignal = 0; + static inline bool setup_sigwinch() { // time for some signalling! struct sigaction new_sa; @@ -427,7 +430,8 @@ namespace detail SetConsoleOutputCP(old_output_cp); } - static inline bool setup_sigwinch(State* currentStateForSignal) + static State* currentStateForSignal = 0; + static inline bool setup_sigwinch() { // on windows we can't really do anything. knowing about window size changes requires using // ReadConsoleInput to poll for events, which we obviously can't do here, or use another @@ -689,7 +693,7 @@ namespace detail #endif } - static size_t getTerminalHeight() + inline size_t getTerminalHeight() { #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO csbi; @@ -1589,7 +1593,6 @@ namespace detail // this is ugly!!! - static State* currentStateForSignal = 0; inline bool read_line(State* st, int promptMode, std::string seed) { constexpr char CTRL_A = '\x01'; @@ -1619,7 +1622,7 @@ namespace detail platform_write(promptMode == 0 ? st->promptString : st->contPromptString); platform_write(seed); - bool didSetSignalHandler = setup_sigwinch(currentStateForSignal); + bool didSetSignalHandler = setup_sigwinch(); auto commit_line = [&](bool refresh = true) { From 7d9e483775e970ad4e32606419f4b9ddab5931c7 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 26 Nov 2019 01:28:37 +0800 Subject: [PATCH 101/129] more repl work --- flax.vcxproj | 275 ---------------------------- libs/SDL2/SDL.flx | 144 +++++++-------- source/include/ast.h | 1 + source/include/typecheck.h | 3 +- source/include/zpr.h | 124 ++++++++----- source/repl/driver.cpp | 3 +- source/repl/execute.cpp | 8 + source/typecheck/classes.cpp | 3 +- source/typecheck/enums.cpp | 3 +- source/typecheck/function.cpp | 18 +- source/typecheck/structs.cpp | 3 +- source/typecheck/toplevel.cpp | 3 +- source/typecheck/traits.cpp | 3 +- source/typecheck/typecheckstate.cpp | 14 +- source/typecheck/unions.cpp | 4 +- source/typecheck/variable.cpp | 9 +- 16 files changed, 202 insertions(+), 416 deletions(-) delete mode 100644 flax.vcxproj diff --git a/flax.vcxproj b/flax.vcxproj deleted file mode 100644 index 0d96dac3..00000000 --- a/flax.vcxproj +++ /dev/null @@ -1,275 +0,0 @@ - - - - - Profiling - x64 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {9FD6A820-341E-44AB-BA7F-A38E05B8E4F1} - Win32Proj - 10.0 - - - - Application - true - v142 - flaxc - - - Application - false - v142 - flaxc - - - Application - false - v142 - flaxc - - - - - - - - - - - - - - - - - D:\Projects\lib\mpir\Debug\include;D:\Projects\lib\mpfr\Debug\include;D:\Projects\lib\llvm\7.0.1\Debug\include;D:\Projects\lib\libffi\Debug\include - - D:\Projects\lib\mpir\Debug\lib;D:\Projects\lib\mpfr\Debug\lib;D:\Projects\lib\llvm\7.0.1\Debug\lib;D:\Projects\lib\libffi\Debug\lib - - D:\Projects\lib\mpir\Release\include;D:\Projects\lib\mpfr\Release\include;D:\Projects\lib\llvm\7.0.1\Release\include;D:\Projects\lib\libffi\Release\include - - D:\Projects\lib\mpir\Release\lib;D:\Projects\lib\mpfr\Release\lib;D:\Projects\lib\llvm\7.0.1\Release\lib;D:\Projects\lib\libffi\Release\lib - - - - $(SolutionDir)source\include;$(SolutionDir)external;$(IncludePath) - $(SolutionDir)build\sysroot\windows\$(Configuration)\ - .\build\sysroot\windows\obj\$(IntDir) - false - - - true - $(SolutionDir)build\sysroot\windows\$(Configuration)\ - false - .\build\sysroot\windows\obj\$(IntDir) - $(SolutionDir)source\include;$(SolutionDir)external;$(IncludePath) - - - false - $(SolutionDir)build\sysroot\windows\$(Configuration)\ - false - .\build\sysroot\windows\obj\$(IntDir) - $(SolutionDir)source\include;$(SolutionDir)external;$(IncludePath) - - - - stdcpp17 - $(SolutionDir)source\include;$(DEPS_DBG_INCLUDES_DIR);%(AdditionalIncludeDirectories) - NotUsing - precompile.h - Level3 - $(IntDir)/%(RelativeDir)/ - precompile.h;%(ForcedIncludeFiles) - 4101;4710;4624;4291;4141;4996;%(DisableSpecificWarnings) - CompileAsCpp - _DEBUG;%(PreprocessorDefinitions) - true - false - ProgramDatabase - Disabled - Disabled - /utf-8 /we4062 /w14062 %(AdditionalOptions) - - - $(DEPS_DBG_LIBS_DIR);%(AdditionalLibraryDirectories) - /ignore:4099 /nodefaultlib:libcmt - - - LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;libffi.lib - LIBCMTD;LIBCMT;%(IgnoreSpecificDefaultLibraries) - true - Default - true - - - - - - - rem call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars64.bat" >nul -robocopy $(ProjectDir)libs $(ProjectDir)build\sysroot\usr\local\lib\flaxlibs /e /nfl /ndl /njh /njs /nc /ns /np -rem cls -rem $(OutputPath)flax.exe -sysroot $(ProjectDir)build/sysroot -run $(ProjectDir)build/tester.flx -rem exit 0 - - - Run the test - - - - - $(SolutionDir)source\include;$(DEPS_REL_INCLUDES_DIR);%(AdditionalIncludeDirectories) - stdcpp17 - NotUsing - precompile.h - Level3 - $(IntDir)/%(RelativeDir)/ - precompile.h;%(ForcedIncludeFiles) - 4101;4710;4624;4291;4141;4996;%(DisableSpecificWarnings) - CompileAsCpp - NDEBUG;%(PreprocessorDefinitions) - MultiThreadedDLL - true - false - MaxSpeed - /utf-8 /we4062 /w14062 %(AdditionalOptions) - - - $(DEPS_REL_LIBS_DIR);%(AdditionalLibraryDirectories) - /ignore:4099 /nodefaultlib:libcmt - - - LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;libffi.lib - LIBCMTD;LIBCMT;%(IgnoreSpecificDefaultLibraries) - false - Default - - - - - - - rem call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars64.bat" >nul -robocopy $(ProjectDir)libs $(ProjectDir)build\sysroot\usr\local\lib\flaxlibs /e /nfl /ndl /njh /njs /nc /ns /np -rem cls -rem $(OutputPath)flax.exe -sysroot $(ProjectDir)build/sysroot -run $(ProjectDir)build/tester.flx -rem exit 0 - - - Run the test - - - - - $(SolutionDir)source\include;$(DEPS_REL_INCLUDES_DIR);%(AdditionalIncludeDirectories) - stdcpp17 - NotUsing - precompile.h - Level3 - $(IntDir)/%(RelativeDir)/ - precompile.h;%(ForcedIncludeFiles) - 4101;4710;4624;4291;4141;4996;%(DisableSpecificWarnings) - CompileAsCpp - NDEBUG;%(PreprocessorDefinitions) - MultiThreadedDLL - true - true - MaxSpeed - /utf-8 /we4062 /w14062 %(AdditionalOptions) - - - $(DEPS_REL_LIBS_DIR);%(AdditionalLibraryDirectories) - /ignore:4099 /nodefaultlib:libcmt - - - LLVMCore.lib;LLVMSupport.lib;LLVMTarget.lib;LLVMPasses.lib;LLVMAnalysis.lib;LLVMGlobalISel.lib;LLVMLibDriver.lib;LLVMLinker.lib;LLVMipo.lib;LLVMBinaryFormat.lib;LLVMMC.lib;LLVMMCJIT.lib;LLVMOrcJIT.lib;LLVMMCParser.lib;LLVMMCDisassembler.lib;LLVMObject.lib;LLVMScalarOpts.lib;LLVMVectorize.lib;LLVMCodegen.lib;LLVMTablegen.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMInstrumentation.lib;LLVMRuntimeDyld.lib;LLVMInstCombine.lib;LLVMInterpreter.lib;LLVMExecutionEngine.lib;LLVMSelectionDAG.lib;LLVMTransformUtils.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMAsmPrinter.lib;LLVMX86AsmPrinter.lib;LLVMProfileData.lib;LLVMX86AsmParser.lib;LLVMX86Info.lib;LLVMX86CodeGen.lib;LLVMX86Utils.lib;LLVMX86Desc.lib;LLVMDlltoolDriver.lib;mpfr.lib;mpir.lib;libffi.lib - LIBCMTD;LIBCMT;%(IgnoreSpecificDefaultLibraries) - false - Default - - - - - - - rem call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars64.bat" >nul -robocopy $(ProjectDir)libs $(ProjectDir)build\sysroot\usr\local\lib\flaxlibs /e /nfl /ndl /njh /njs /nc /ns /np -rem cls -rem $(OutputPath)flax.exe -sysroot $(ProjectDir)build/sysroot -run $(ProjectDir)build/tester.flx -rem exit 0 - - - Run the test - - - - - - - - - - - - - - - - source\%(RecursiveDir)%(Filename)%(Extension) - - - source\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - - - - - - - - - - - - - source\include\%(RecursiveDir)%(Filename)%(Extension) - - - source\include\%(RecursiveDir)%(Filename)%(Extension) - - - source\external\%(RecursiveDir)%(Filename)%(Extension) - - - source\external\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libs/SDL2/SDL.flx b/libs/SDL2/SDL.flx index ec9f193a..d8403ab4 100644 --- a/libs/SDL2/SDL.flx +++ b/libs/SDL2/SDL.flx @@ -2,7 +2,7 @@ // Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. -import "Keyboard" +import "Keyboard.flx" public namespace SDL { @@ -46,71 +46,71 @@ public namespace SDL public enum EventType: u32 { - case FIRSTEVENT = 0 - - case Quit = 0x100 - case App_Terminating - case App_LowMemory - case App_WillEnterBackground - case App_DidEnterBackground - case App_WillEnterForeground - case App_DidEnterForeground - - case WindowEvent = 0x200 - case SysWMEvent - - case KeyDown = 0x300 - case KeyUp - case TextEditing - case TextInput - case KeymapChanged - - case Mouse_Motion = 0x400 - case Mouse_ButtonDown - case Mouse_ButtonUp - case Mouse_Wheel - - case Joy_AxisMotion = 0x600 - case Joy_BallMotion - case Joy_HatMotion - case Joy_ButtonDown - case Joy_ButtonUp - case Joy_DeviceAdded - case Joy_DeviceRemoved - - case Controller_AxisMotion = 0x650 - case Controller_ButtonDown - case Controller_ButtonUp - case Controller_DeviceAdded - case Controller_DeviceRemoved - case Controller_DeviceRemapped - - case FingerDown = 0x700 - case FingerUp - case FingerMotion - - case DollarGesture = 0x800 - case DollarRecord - case MultiGesture - - case ClipboardUpdate = 0x900 - - case DropFile = 0x1000 - - case Audio_DeviceAdded = 0x1100 - case Audio_DeviceRemoved - - case Render_TargetsReset = 0x2000 - case Render_DeviceReset - - case USEREVENT = 0x8000 - case LASTEVENT = 0xFFFF + case FIRSTEVENT = 0 + + case Quit = 0x100 + case App_Terminating = 0x101 + case App_LowMemory = 0x102 + case App_WillEnterBackground = 0x103 + case App_DidEnterBackground = 0x104 + case App_WillEnterForeground = 0x105 + case App_DidEnterForeground = 0x106 + + case WindowEvent = 0x200 + case SysWMEvent = 0x201 + + case KeyDown = 0x300 + case KeyUp = 0x301 + case TextEditing = 0x302 + case TextInput = 0x303 + case KeymapChanged = 0x304 + + case Mouse_Motion = 0x400 + case Mouse_ButtonDown = 0x401 + case Mouse_ButtonUp = 0x402 + case Mouse_Wheel = 0x403 + + case Joy_AxisMotion = 0x600 + case Joy_BallMotion = 0x601 + case Joy_HatMotion = 0x602 + case Joy_ButtonDown = 0x603 + case Joy_ButtonUp = 0x604 + case Joy_DeviceAdded = 0x605 + case Joy_DeviceRemoved = 0x606 + + case Controller_AxisMotion = 0x650 + case Controller_ButtonDown = 0x651 + case Controller_ButtonUp = 0x652 + case Controller_DeviceAdded = 0x653 + case Controller_DeviceRemoved = 0x654 + case Controller_DeviceRemapped = 0x655 + + case FingerDown = 0x700 + case FingerUp = 0x701 + case FingerMotion = 0x702 + + case DollarGesture = 0x800 + case DollarRecord = 0x801 + case MultiGesture = 0x802 + + case ClipboardUpdate = 0x900 + + case DropFile = 0x1000 + + case Audio_DeviceAdded = 0x1100 + case Audio_DeviceRemoved = 0x1101 + + case Render_TargetsReset = 0x2000 + case Render_DeviceReset = 0x2001 + + case USEREVENT = 0x8000 + case LASTEVENT = 0xFFFF } public struct Event { - var type: SDL.EventType - var lol: i8[52] + type: SDL::EventType + lol: [i8: 52] } public class KeyboardEvent @@ -118,32 +118,32 @@ public namespace SDL public struct KeySym { // SDL_Scancode scancode - var scancode: u32 - var sym: SDL.Key + scancode: u32 + sym: SDL::Key - var mod: u16 - var unused: u32 + mod: u16 + unused: u32 } - var type: SDL.EventType + var type: SDL::EventType var timestamp: u32 var windowid: u32 var state: u8 var repeat: u8 var padding: u16 - var keysym: SDL.KeyboardEvent.KeySym + var keysym: SDL::KeyboardEvent::KeySym } } public ffi fn SDL_Init(flags: u32) -> i32 -public ffi fn SDL_CreateWindow(title: i8*, xpos: i32, ypos: i32, width: i32, height: i32, flags: u32) -> void* +public ffi fn SDL_CreateWindow(title: &i8, xpos: i32, ypos: i32, width: i32, height: i32, flags: u32) -> &void public ffi fn SDL_GL_SetAttribute(attr: i32, value: i32) -public ffi fn SDL_PollEvent(event: SDL.Event*) -> i32 +public ffi fn SDL_PollEvent(event: &SDL::Event) -> i32 // opengl stuff -public ffi fn SDL_GL_CreateContext(window: void*) -> void* -public ffi fn SDL_GL_SwapWindow(window: void*) -> void +public ffi fn SDL_GL_CreateContext(window: &void) -> &void +public ffi fn SDL_GL_SwapWindow(window: &void) -> void diff --git a/source/include/ast.h b/source/include/ast.h index 8bd94c86..2dde3d54 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -102,6 +102,7 @@ namespace ast // the real, original scope of the type. //? we set this in typecheck/toplevel.cpp when generating the declarations. //? for methods & nested types, we set them in structs.cpp/classes.cpp + //? in repl mode, we set this manually. std::vector realScope; }; diff --git a/source/include/typecheck.h b/source/include/typecheck.h index cc67d966..6b7cb156 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -183,7 +183,8 @@ namespace sst StateTree* getTreeOfScope(const std::vector& scope); std::vector getDefinitionsWithName(const std::string& name, StateTree* tree = 0); - bool checkForShadowingOrConflictingDefinition(Defn* def, std::function checkConflicting, StateTree* tree = 0); + ErrorMsg* checkForShadowingOrConflictingDefinition(Defn* def, + std::function checkConflicting, StateTree* tree = 0); fir::Type* getBinaryOpResultType(fir::Type* a, fir::Type* b, const std::string& op, sst::FunctionDefn** overloadFn = 0); diff --git a/source/include/zpr.h b/source/include/zpr.h index 53c46b6a..8885ba98 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -45,7 +46,7 @@ namespace zpr constexpr static int LENGTH_PTRDIFF_T = 8; }; - template + template struct print_formatter { template @@ -59,7 +60,7 @@ namespace zpr // we make a inner type has_formatter which is more descriptive, and since we only make this // error when we try to instantiate the base, any specialisations don't even need to care! - static_assert(has_formatter::value, "no formatter defined for type!"); + static_assert(true || has_formatter::value, "no formatter defined for type!"); }; @@ -204,51 +205,100 @@ namespace zpr .append(sprint(fmt, xs...)); } - template + // we need separate versions of these functions that only enable if the argument is + // a pointer, THEN check if the specifier is 'p', THEN do the void* thing. + + template + constexpr bool _stupid_type_tester(decltype(!std::is_pointer_v> + || &print_formatter>::print)*) { return true; } + + template + constexpr bool _stupid_type_tester(...) { return false; } + + + template (0)>> std::string _consume_neither_sprint(const format_args& args, const char* fmt, T&& x, Args&&... xs) { - return print_formatter>() - .print(x, args) - .append(sprint(fmt, xs...)); + return print_formatter>().print(x, args).append(sprint(fmt, xs...)); } + template (0)>, typename F = void*> + std::string _consume_neither_sprint(const format_args& args, const char* fmt, T&& x, Args&&... xs) + { + return print_formatter().print(x, args).append(sprint(fmt, xs...)); + } + + + template >>> + typename = std::enable_if_t> && _stupid_type_tester(0)> + > std::string _consume_width_sprint(format_args args, const char* fmt, W&& width, T&& x, Args&&... xs) { args.width = width; + return print_formatter>().print(x, args).append(sprint(fmt, xs...)); + } - return print_formatter>() - .print(x, args) - .append(sprint(fmt, xs...)); + template > && !_stupid_type_tester(0)>, + typename F = void* + > + std::string _consume_width_sprint(format_args args, const char* fmt, W&& width, T&& x, Args&&... xs) + { + args.width = width; + return print_formatter().print(x, args).append(sprint(fmt, xs...)); } + template >>> + typename = std::enable_if_t> && _stupid_type_tester(0)> + > std::string _consume_prec_sprint(format_args args, const char* fmt, P&& prec, T&& x, Args&&... xs) { args.precision = prec; + return print_formatter>().print(x, args).append(sprint(fmt, xs...)); + } - return print_formatter>() - .print(x, args) - .append(sprint(fmt, xs...)); + template > && !_stupid_type_tester(0)>, + typename F = void* + > + std::string _consume_prec_sprint(format_args args, const char* fmt, P&& prec, T&& x, Args&&... xs) + { + args.precision = prec; + return print_formatter().print(x, args).append(sprint(fmt, xs...)); } + + + + template > - && std::is_integral_v>>> + typename = std::enable_if_t> + && std::is_integral_v> && _stupid_type_tester(0)> + > std::string _consume_both_sprint(format_args args, const char* fmt, W&& width, P&& prec, T&& x, Args&&... xs) { args.width = width; args.precision = prec; + return print_formatter>().print(x, args).append(sprint(fmt, xs...)); + } - return print_formatter>() - .print(x, args) - .append(sprint(fmt, xs...)); + template > + && std::is_integral_v> && !_stupid_type_tester(0)>, + typename F = void* + > + std::string _consume_both_sprint(format_args args, const char* fmt, W&& width, P&& prec, T&& x, Args&&... xs) + { + args.width = width; + args.precision = prec; + return print_formatter().print(x, args).append(sprint(fmt, xs...)); } + template std::string sprint(const char* fmt, Args&&... xs) { @@ -357,29 +407,17 @@ namespace zpr const char* fmt_str = 0; auto spec = args.specifier; - switch(args.length) - { - case format_args::LENGTH_SHORT_SHORT: fmt_str = (spec == 'u' - ? "%hhu" : (spec == 'x' ? "%hhx" : (spec == 'X' ? "%hhX" : "%hhd"))); break; - case format_args::LENGTH_SHORT: fmt_str = (spec == 'u' - ? "%hu" : (spec == 'x' ? "%hx" : (spec == 'X' ? "%hX" : "%hd"))); break; - case format_args::LENGTH_LONG: fmt_str = (spec == 'u' - ? "%lu" : (spec == 'x' ? "%lx" : (spec == 'X' ? "%lX" : "%ld"))); break; - case format_args::LENGTH_LONG_LONG: fmt_str = (spec == 'u' - ? "%llu" : (spec == 'x' ? "%llx" : (spec == 'X' ? "%llX" : "%lld"))); break; - case format_args::LENGTH_INTMAX_T: fmt_str = (spec == 'u' - ? "%ju" : (spec == 'x' ? "%jx" : (spec == 'X' ? "%jX" : "%jd"))); break; - case format_args::LENGTH_SIZE_T: fmt_str = (spec == 'u' - ? "%zu" : (spec == 'x' ? "%zx" : (spec == 'X' ? "%zX" : "%zd"))); break; - case format_args::LENGTH_PTRDIFF_T: fmt_str = (spec == 'u' - ? "%tu" : (spec == 'x' ? "%tx" : (spec == 'X' ? "%tX" : "%td"))); break; - - case format_args::LENGTH_DEFAULT: [[fallthrough]]; - default: - fmt_str = (spec == 'u' ? "%u" : (spec == 'x' ? "%x" : (spec == 'X' ? "%X" : "%d"))); - break; - } - + static std::map len_specs = { + { format_args::LENGTH_SHORT_SHORT, "hh" }, + { format_args::LENGTH_SHORT, "h" }, + { format_args::LENGTH_LONG, "l" }, + { format_args::LENGTH_LONG_LONG, "ll" }, + { format_args::LENGTH_INTMAX_T, "j" }, + { format_args::LENGTH_SIZE_T, "z" }, + { format_args::LENGTH_PTRDIFF_T, "t" } + }; + + fmt_str = ("%" + len_specs[args.length] + spec).c_str(); digits_len = snprintf(&buf[0], 64, fmt_str, x); // sadly, we must cheat here as well, because osx doesn't bloody have charconv (STILL)? @@ -591,7 +629,7 @@ namespace zpr template struct print_formatter) + (std::is_same_v>) >::type> { std::string print(const T& x, format_args args) diff --git a/source/repl/driver.cpp b/source/repl/driver.cpp index 28bcd127..c89504a0 100644 --- a/source/repl/driver.cpp +++ b/source/repl/driver.cpp @@ -60,7 +60,8 @@ namespace repl if(input.empty()) continue; - if(input[0] == ':') + // commands start with ':', but also allow '::' path-prefix. + if(input[0] == ':' && input.find("::") != 0) { auto quit = repl::runCommand(input.substr(1), &st); if(quit) break; diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index 380ce5cf..f3993391 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -121,6 +121,11 @@ namespace repl try { + // note: usually, visitDeclarables in the top-level typecheck will set the realScope. + // BUT, since we're not doing that, we must set it manually! + if(auto def = dcast(ast::Parameterisable, stmt); def) + def->realScope = state->fs->getCurrentScope(); + tcr = stmt->typecheck(state->fs); } catch(ErrorException& ee) @@ -148,6 +153,9 @@ namespace repl bool processLine(const std::string& line) { + // before we begin, bring us into a new namespace. + state->fs->pushAnonymousTree(); + bool needmore = false; auto stmt = repl::parseAndTypecheck(line, &needmore); if(!stmt) diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index 78a6a406..e3904eae 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -83,7 +83,8 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* } } - fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) + return TCResult(err); // add it first so we can use it in the method bodies, // and make pointers to it diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index 35f09d82..8c3d65e9 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -45,7 +45,8 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->original = this; defn->type = fir::EnumType::getEmpty(); - fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) + return TCResult(err); fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 7c86fdfe..d75a1b27 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -87,13 +87,13 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* error(defn, "only methods of a type can be marked as mutating with 'mut'"); } - bool conflicts = fs->checkForShadowingOrConflictingDefinition(defn, [defn](sst::TypecheckState* fs, sst::Stmt* other) -> bool { + auto conflict_err = fs->checkForShadowingOrConflictingDefinition(defn, [defn](sst::TypecheckState* fs, sst::Stmt* other) -> bool { - if(auto decl = dcast(sst::FunctionDecl, other)) + if(auto oth = dcast(sst::FunctionDecl, other)) { // make sure we didn't fuck up somewhere - iceAssert(decl->id.name == defn->id.name); - return fs->isDuplicateOverload(defn->params, decl->params); + iceAssert(oth->id.name == defn->id.name); + return fs->isDuplicateOverload(defn->params, oth->params); } else { @@ -102,8 +102,8 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* } }); - if(conflicts) - error(this, "conflicting"); + if(conflict_err) + return TCResult(conflict_err); if(!defn->type->containsPlaceholders()) fs->getTreeOfScope(this->realScope)->addDefinition(this->name, defn, gmaps); @@ -214,7 +214,7 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf defn->type = fir::FunctionType::get(util::map(ps, [](const FnParam& p) -> auto { return p.type; }), retty); - bool conflicts = fs->checkForShadowingOrConflictingDefinition(defn, [defn](sst::TypecheckState* fs, sst::Stmt* other) -> bool { + auto conflict_err = fs->checkForShadowingOrConflictingDefinition(defn, [defn](sst::TypecheckState* fs, sst::Stmt* other) -> bool { if(auto decl = dcast(sst::FunctionDecl, other)) { @@ -236,8 +236,8 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf } }); - if(conflicts) - error(this, "conflicting"); + if(conflict_err) + return TCResult(conflict_err); this->generatedDecl = defn; diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index 284dd6d4..1316f07a 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -150,7 +150,8 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type auto str = fir::StructType::createWithoutBody(defn->id, /* isPacked: */ this->attrs.has(attr::PACKED)); defn->type = str; - fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) + return TCResult(err); // add it first so we can use it in the method bodies, // and make pointers to it diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 1ebbb6f6..93bb9476 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -471,7 +471,8 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer td->visibility = this->visibility; - fs->checkForShadowingOrConflictingDefinition(td, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }, tree->parent); + if(auto err = fs->checkForShadowingOrConflictingDefinition(td, [](auto, auto) -> bool { return true; }, tree->parent)) + return TCResult(err); tree->parent->addDefinition(tree->topLevelFilename, td->id.name, td); } diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index ebca07ab..be2d6953 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -37,7 +37,8 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* auto str = fir::TraitType::create(defn->id); defn->type = str; - fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) + return TCResult(err); // add it first so we can use it in the method bodies, // and make pointers to it diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index f2057593..7b7862aa 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -406,7 +406,7 @@ namespace sst return ret; } - bool TypecheckState::checkForShadowingOrConflictingDefinition(Defn* defn, + ErrorMsg* TypecheckState::checkForShadowingOrConflictingDefinition(Defn* defn, std::function conflictCheckCallback, StateTree* tree) { if(tree == 0) @@ -451,6 +451,7 @@ namespace sst return err; }; + // ok, now check only the current scope auto defs = tree->getDefinitionsWithName(defn->id.name); @@ -473,7 +474,7 @@ namespace sst } } - errs->postAndQuit(); + return errs; } } @@ -501,20 +502,21 @@ namespace sst ); if(newgds.size() > 0) - makeTheError(fn, fn->id.name, fn->getKind(), newgds)->postAndQuit(); + return makeTheError(fn, fn->id.name, fn->getKind(), newgds); } else { // assume everything conflicts, since functions are the only thing that can overload. - makeTheError(defn, defn->id.name, defn->getKind(), + return makeTheError(defn, defn->id.name, defn->getKind(), util::map(gdefs, [](ast::Parameterisable* d) -> std::pair { return std::make_pair(d, d->getKind()); }) - )->postAndQuit(); + ); } } - return false; + // no error. + return nullptr; } void TypecheckState::pushAnonymousTree() diff --git a/source/typecheck/unions.cpp b/source/typecheck/unions.cpp index 813d5237..1ab94a90 100644 --- a/source/typecheck/unions.cpp +++ b/source/typecheck/unions.cpp @@ -44,7 +44,9 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* if(israw) defn->type = fir::RawUnionType::createWithoutBody(defn->id); else defn->type = fir::UnionType::createWithoutBody(defn->id); - fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + + if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) + return TCResult(err); fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 430c33fe..1e2cfa7b 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -229,7 +229,9 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(succs.empty()) { - auto errs = SimpleError::make(this->loc, "no definition of '%s'%s", this->name, infer ? strprintf(" matching type '%s'", infer) : ""); + auto errs = SimpleError::make(this->loc, "no definition of '%s'%s", this->name, + infer ? strprintf(" matching type '%s'", infer) : ""); + for(const auto& v : succs) errs->append(v.second.error()); @@ -240,7 +242,7 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) auto errs = SimpleError::make(this->loc, "ambiguous reference to '%s'", this->name); for(const auto& v : succs) - errs->append(SimpleError::make(MsgType::Note, v.first->loc, "potential target here:")); + errs->append(SimpleError::make(MsgType::Note, v.first->loc, "potential target here:", v.first)); return TCResult(errs); } @@ -342,7 +344,8 @@ TCResult ast::VarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) //* for variables, as long as the name matches, we conflict. - fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); + if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) + return TCResult(err); // check the defn if(this->initialiser) From 44fbfb7950dcf09ce0271164d3eb82561fa52ead Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 27 Nov 2019 22:54:13 +0800 Subject: [PATCH 102/129] scope handling starts to crumble like it's december 1991 --- build/gltest.flx | 82 ++-- build/tmp/repro_1.flx | 12 + build/tmp/repro_2.flx | 17 + libs/SDL2/Keyboard.flx | 509 +++++++++++++------------ libs/SDL2/SDL.flx | 260 ++++++------- source/fir/Types/EnumType.cpp | 22 +- source/frontend/parser/controlflow.cpp | 23 -- source/frontend/parser/type.cpp | 3 + source/include/ir/type.h | 2 +- source/typecheck/arithmetic.cpp | 5 + source/typecheck/enums.cpp | 8 +- source/typecheck/typecheckstate.cpp | 2 +- 12 files changed, 483 insertions(+), 462 deletions(-) create mode 100644 build/tmp/repro_1.flx create mode 100644 build/tmp/repro_2.flx diff --git a/build/gltest.flx b/build/gltest.flx index e864e48c..1616639e 100644 --- a/build/gltest.flx +++ b/build/gltest.flx @@ -3,10 +3,10 @@ // Licensed under the Apache License Version 2.0. // import Foundation -import "OpenGL/GL" -import "SDL2/SDL" +import "OpenGL/GL.flx" +import "SDL2/SDL.flx" as _ -import "LibCInterface" +import libc as _ ffi fn rand() -> i32 @@ -35,9 +35,9 @@ fn clamp(x: f64, lower: f64, upper: f64) -> f64 -public fn main(argc: i32, argv: i8**) -> int +public fn main(argc: i32, argv: &&i8) -> int { - SDL_Init(SDL.INIT_EVERYTHING) + SDL_Init(INIT_EVERYTHING) let mw = SDL_CreateWindow("test", 805240832, 805240832, 640, 480, 2) if mw == null @@ -51,29 +51,29 @@ public fn main(argc: i32, argv: i8**) -> int let glc = SDL_GL_CreateContext(mw) - SDL_GL_SetAttribute(SDL.GL_MULTISAMPLEBUFFERS, 1) - SDL_GL_SetAttribute(SDL.GL_MULTISAMPLESAMPLES, 2) + SDL_GL_SetAttribute(GL_MULTISAMPLEBUFFERS, 1) + SDL_GL_SetAttribute(GL_MULTISAMPLESAMPLES, 2) - glClearColor(0, 0, 0, 0) + GL::glClearColor(0, 0, 0, 0) - glMatrixMode(GL.PROJECTION) - glLoadIdentity() - gluPerspective(45.0, (640.0 / 480.0), 0.1, 100.0) + GL::glMatrixMode(GL::GL::PROJECTION) + GL::glLoadIdentity() + GL::gluPerspective(45.0, (640.0 / 480.0), 0.1, 100.0) - glMatrixMode(GL.MODELVIEW) - glLoadIdentity() + GL::glMatrixMode(GL::GL::MODELVIEW) + GL::glLoadIdentity() - glEnableClientState(GL.VERTEX_ARRAY) - glEnableClientState(GL.TEXTURE_COORD_ARRAY) - glEnableClientState(GL.COLOR_ARRAY) - glEnableClientState(GL.NORMAL_ARRAY) + GL::glEnableClientState(GL::GL::VERTEX_ARRAY) + GL::glEnableClientState(GL::GL::TEXTURE_COORD_ARRAY) + GL::glEnableClientState(GL::GL::COLOR_ARRAY) + GL::glEnableClientState(GL::GL::NORMAL_ARRAY) - glEnable(GL.MULTISAMPLE) - glEnable(GL.DEPTH_TEST) - glDepthFunc(GL.LESS) + GL::glEnable(GL::GL::MULTISAMPLE) + GL::glEnable(GL::GL::DEPTH_TEST) + GL::glDepthFunc(GL::GL::LESS) - glBlendColor(1.0, 1.0, 1.0, 1.0) + GL::glBlendColor(1.0, 1.0, 1.0, 1.0) var rx: f64 = 0 var ry: f64 = 0 @@ -94,8 +94,8 @@ public fn main(argc: i32, argv: i8**) -> int ] // var colours: f64[36] - var colours = alloc[48] f64 - defer dealloc colours + var colours = alloc f64[48] + defer free colours var i = 0 while i < 48 @@ -107,7 +107,7 @@ public fn main(argc: i32, argv: i8**) -> int i += 4 } - let indices: u8[60] = [ + let indices: [u8: 60] = [ 1, 2, 6, 1, 7, 2, 3, 4, 5, @@ -161,22 +161,22 @@ public fn main(argc: i32, argv: i8**) -> int let stderr = fdopen(2, "a") while run { - glClear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT) - glClearColor(0.109, 0.109, 0.109, 1.0) - glTranslated(0.0, 0.0, -5.0) + GL::glClear(GL::GL::COLOR_BUFFER_BIT | GL::GL::DEPTH_BUFFER_BIT) + GL::glClearColor(0.109, 0.109, 0.109, 1.0) + GL::glTranslated(0.0, 0.0, -5.0) - glRotated(rx, frand(0, 1), 0, 0) - glRotated(ry, 0, frand(0, 1), 0) - glRotated(rz, 0, 0, frand(0, 1)) + GL::glRotated(rx, frand(0, 1), 0, 0) + GL::glRotated(ry, 0, frand(0, 1), 0) + GL::glRotated(rz, 0, 0, frand(0, 1)) - glVertexPointer(3, GL.DOUBLE, 0, vertices.data) - glColorPointer(4, GL.DOUBLE, 0, colours.data) - glNormalPointer(GL.DOUBLE, 0, normals.data) + GL::glVertexPointer(3, GL::GL::DOUBLE, 0, vertices.ptr as &void) + GL::glColorPointer(4, GL::GL::DOUBLE, 0, colours.ptr as &void) + GL::glNormalPointer(GL::GL::DOUBLE, 0, normals.ptr as &void) - glDrawElements(GL.TRIANGLES, 60, GL.UNSIGNED_BYTE, indices as void*) + GL::glDrawElements(GL::GL::TRIANGLES, 60, GL::GL::UNSIGNED_BYTE, indices as &void) - glLoadIdentity() + GL::glLoadIdentity() SDL_GL_SwapWindow(mw) @@ -186,23 +186,23 @@ public fn main(argc: i32, argv: i8**) -> int ry += sry * speed rz += srz * speed - var e: SDL.Event + var e: Event while SDL_PollEvent(&e) == 1 { - if e.type == SDL.EventType.Quit + if e.type == EventType::Quit { printf("\nexiting\n") run = false } - else if e.type == SDL.EventType.KeyUp + else if e.type == EventType::KeyUp { - if (&e as SDL.KeyboardEvent*).keysym.sym == SDL.Key.R + if (&e as &SDL.KeyboardEvent).keysym.sym == SDL.Key.R { srx = frand(-2.5, 2.5) sry = frand(-2.5, 2.5) srz = frand(-2.5, 2.5) } - else if (&e as SDL.KeyboardEvent*).keysym.sym == SDL.Key.F + else if (&e as &SDL.KeyboardEvent).keysym.sym == SDL.Key.F { rx = 0 ry = 0 @@ -217,7 +217,7 @@ public fn main(argc: i32, argv: i8**) -> int } else if e.type == SDL.EventType.KeyDown { - let ke = *(&e as SDL.KeyboardEvent*) + let ke = *(&e as &SDL.KeyboardEvent) if ke.keysym.sym == SDL.Key.Space { diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx new file mode 100644 index 00000000..39b3dac4 --- /dev/null +++ b/build/tmp/repro_1.flx @@ -0,0 +1,12 @@ +// repro_1.flx +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +import libc as _ +import repro_2 as _ + +@entry fn main() +{ + // printf("four = %d\n", repro_2::Foo::FOUR.value); + printf("%d\n", Bar::bla(3)); +} diff --git a/build/tmp/repro_2.flx b/build/tmp/repro_2.flx new file mode 100644 index 00000000..0f2df1e8 --- /dev/null +++ b/build/tmp/repro_2.flx @@ -0,0 +1,17 @@ +// repro_2.flx +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +public enum Foo: i32 +{ + case ONE = 1 + case TWO = 2 + case FOUR = 4 +} + +public class Bar +{ + init() { } + + static fn bla(x: int) -> int => x * x +} diff --git a/libs/SDL2/Keyboard.flx b/libs/SDL2/Keyboard.flx index e0b6ba05..5ddcf90f 100644 --- a/libs/SDL2/Keyboard.flx +++ b/libs/SDL2/Keyboard.flx @@ -2,277 +2,278 @@ // Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. -public namespace SDL +export SDL::Keyboard + +public enum Key: u32 { - public enum Key: u32 - { - case UNKNOWN = 0 + case UNKNOWN = 0 + + case Backspace = 0x08 + case Tab = 0x09 + case Return = 0x0D + case Escape = 0x1B - case Backspace = 0x08 - case Tab = 0x09 - case Return = 0x0D - case Escape = 0x1B + case Space = 0x20 + case Exclaim = 0x21 + case DoubleQuote = 0x21 + case Hash = 0x22 + case Dollar = 0x23 + case Percent = 0x24 + case Ampersand = 0x25 + case Quote = 0x26 + case LeftParen = 0x27 + case RightParen = 0x28 + case Asterisk = 0x29 + case Plus = 0x2A + case Comma = 0x2B + case Minus = 0x2C + case Period = 0x2D + case Slash = 0x2E + case Zero = 0x2F + case One = 0x30 + case Two = 0x31 + case Three = 0x32 + case Four = 0x33 + case Five = 0x34 + case Six = 0x35 + case Seven = 0x36 + case Eight = 0x37 + case Nine = 0x38 + case Ten = 0x39 + case Colon = 0x3A + case Semicolon = 0x3B + case Less = 0x3C + case Equals = 0x3D + case Greater = 0x3E + case Question = 0x3F + case At = 0x40 - case Space = 0x20 - case Exclaim - case DoubleQuote - case Hash - case Dollar - case Percent - case Ampersand - case Quote - case LeftParen - case RightParen - case Asterisk - case Plus - case Comma - case Minus - case Period - case Slash - case Zero - case One - case Two - case Three - case Four - case Five - case Six - case Seven - case Eight - case Nine - case Ten - case Colon - case Semicolon - case Less - case Equals - case Greater - case Question - case At + // Skip uppercase letters + case LeftBracket = 0x41 + case Backslash = 0x42 + case RightBracket = 0x45 + case Caret = 0x46 + case Underscore = 0x47 + case Backquote = 0x48 - // Skip uppercase letters - case LeftBracket - case Backslash - case RightBracket - case Caret - case Underscore - case Backquote - case A = 0x61 - case B - case C - case D - case E - case F - case G - case H - case I - case J - case K - case L - case M - case N - case O - case P - case Q - case R - case S - case T - case U - case V - case W - case X - case Y - case Z + case A = 0x61 + case B = 0x62 + case C = 0x63 + case D = 0x64 + case E = 0x65 + case F = 0x66 + case G = 0x67 + case H = 0x68 + case I = 0x69 + case J = 0x6A + case K = 0x6B + case L = 0x6C + case M = 0x6D + case N = 0x6E + case O = 0x6F + case P = 0x70 + case Q = 0x71 + case R = 0x72 + case S = 0x73 + case T = 0x74 + case U = 0x75 + case V = 0x76 + case W = 0x77 + case X = 0x78 + case Y = 0x79 + case Z = 0x7A - /*CAPSLOCK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CAPSLOCK), + /*CAPSLOCK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CAPSLOCK), - F1 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F1), - F2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F2), - F3 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F3), - F4 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F4), - F5 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F5), - F6 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F6), - F7 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F7), - F8 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F8), - F9 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F9), - F10 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F10), - F11 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F11), - F12 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F12), + F1 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F1), + F2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F2), + F3 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F3), + F4 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F4), + F5 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F5), + F6 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F6), + F7 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F7), + F8 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F8), + F9 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F9), + F10 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F10), + F11 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F11), + F12 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F12), - PRINTSCREEN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRINTSCREEN), - SCROLLLOCK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SCROLLLOCK), - PAUSE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAUSE), - INSERT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_INSERT), - HOME = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HOME), - PAGEUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEUP), - DELETE = '\177', - END = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_END), - PAGEDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEDOWN), - RIGHT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RIGHT), - LEFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LEFT), - DOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DOWN), - UP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UP), + PRINTSCREEN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRINTSCREEN), + SCROLLLOCK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SCROLLLOCK), + PAUSE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAUSE), + INSERT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_INSERT), + HOME = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HOME), + PAGEUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEUP), + DELETE = '\177', + END = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_END), + PAGEDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEDOWN), + RIGHT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RIGHT), + LEFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LEFT), + DOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DOWN), + UP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UP), - NUMLOCKCLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_NUMLOCKCLEAR), - KP_DIVIDE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DIVIDE), - KP_MULTIPLY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MULTIPLY), - KP_MINUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MINUS), - KP_PLUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUS), - KP_ENTER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_ENTER), - KP_1 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_1), - KP_2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_2), - KP_3 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_3), - KP_4 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_4), - KP_5 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_5), - KP_6 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_6), - KP_7 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_7), - KP_8 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_8), - KP_9 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_9), - KP_0 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_0), - KP_PERIOD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERIOD), + NUMLOCKCLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_NUMLOCKCLEAR), + KP_DIVIDE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DIVIDE), + KP_MULTIPLY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MULTIPLY), + KP_MINUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MINUS), + KP_PLUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUS), + KP_ENTER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_ENTER), + KP_1 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_1), + KP_2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_2), + KP_3 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_3), + KP_4 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_4), + KP_5 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_5), + KP_6 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_6), + KP_7 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_7), + KP_8 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_8), + KP_9 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_9), + KP_0 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_0), + KP_PERIOD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERIOD), - APPLICATION = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_APPLICATION), - POWER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_POWER), - KP_EQUALS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALS), - F13 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F13), - F14 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F14), - F15 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F15), - F16 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F16), - F17 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F17), - F18 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F18), - F19 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F19), - F20 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F20), - F21 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F21), - F22 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F22), - F23 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F23), - F24 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F24), - EXECUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXECUTE), - HELP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HELP), - MENU = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MENU), - SELECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SELECT), - STOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_STOP), - AGAIN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AGAIN), - UNDO = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UNDO), - CUT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CUT), - COPY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COPY), - PASTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PASTE), - FIND = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_FIND), - MUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MUTE), - VOLUMEUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEUP), - VOLUMEDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEDOWN), - KP_COMMA = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COMMA), - KP_EQUALSAS400 = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALSAS400), + APPLICATION = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_APPLICATION), + POWER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_POWER), + KP_EQUALS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALS), + F13 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F13), + F14 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F14), + F15 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F15), + F16 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F16), + F17 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F17), + F18 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F18), + F19 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F19), + F20 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F20), + F21 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F21), + F22 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F22), + F23 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F23), + F24 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F24), + EXECUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXECUTE), + HELP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HELP), + MENU = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MENU), + SELECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SELECT), + STOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_STOP), + AGAIN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AGAIN), + UNDO = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UNDO), + CUT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CUT), + COPY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COPY), + PASTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PASTE), + FIND = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_FIND), + MUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MUTE), + VOLUMEUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEUP), + VOLUMEDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEDOWN), + KP_COMMA = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COMMA), + KP_EQUALSAS400 = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALSAS400), - ALTERASE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_ALTERASE), - SYSREQ = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SYSREQ), - CANCEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CANCEL), - CLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEAR), - PRIOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRIOR), - RETURN2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RETURN2), - SEPARATOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SEPARATOR), - OUT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OUT), - OPER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OPER), - CLEARAGAIN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEARAGAIN), - CRSEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CRSEL), - EXSEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXSEL), + ALTERASE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_ALTERASE), + SYSREQ = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SYSREQ), + CANCEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CANCEL), + CLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEAR), + PRIOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRIOR), + RETURN2 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RETURN2), + SEPARATOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SEPARATOR), + OUT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OUT), + OPER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OPER), + CLEARAGAIN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEARAGAIN), + CRSEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CRSEL), + EXSEL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXSEL), - KP_00 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_00), - KP_000 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_000), - THOUSANDSSEPARATOR = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_THOUSANDSSEPARATOR), - DECIMALSEPARATOR = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DECIMALSEPARATOR), - CURRENCYUNIT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYUNIT), - CURRENCYSUBUNIT = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYSUBUNIT), - KP_LEFTPAREN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTPAREN), - KP_RIGHTPAREN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTPAREN), - KP_LEFTBRACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTBRACE), - KP_RIGHTBRACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTBRACE), - KP_TAB = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_TAB), - KP_BACKSPACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BACKSPACE), - KP_A = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_A), - KP_B = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_B), - KP_C = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_C), - KP_D = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_D), - KP_E = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_E), - KP_F = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_F), - KP_XOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_XOR), - KP_POWER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_POWER), - KP_PERCENT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERCENT), - KP_LESS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LESS), - KP_GREATER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_GREATER), - KP_AMPERSAND = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AMPERSAND), - KP_DBLAMPERSAND = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLAMPERSAND), - KP_VERTICALBAR = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_VERTICALBAR), - KP_DBLVERTICALBAR = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLVERTICALBAR), - KP_COLON = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COLON), - KP_HASH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HASH), - KP_SPACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_SPACE), - KP_AT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AT), - KP_EXCLAM = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EXCLAM), - KP_MEMSTORE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSTORE), - KP_MEMRECALL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMRECALL), - KP_MEMCLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMCLEAR), - KP_MEMADD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMADD), - KP_MEMSUBTRACT = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSUBTRACT), - KP_MEMMULTIPLY = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMMULTIPLY), - KP_MEMDIVIDE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMDIVIDE), - KP_PLUSMINUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUSMINUS), - KP_CLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEAR), - KP_CLEARENTRY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEARENTRY), - KP_BINARY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BINARY), - KP_OCTAL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_OCTAL), - KP_DECIMAL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DECIMAL), - KP_HEXADECIMAL = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HEXADECIMAL), + KP_00 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_00), + KP_000 = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_000), + THOUSANDSSEPARATOR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_THOUSANDSSEPARATOR), + DECIMALSEPARATOR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DECIMALSEPARATOR), + CURRENCYUNIT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYUNIT), + CURRENCYSUBUNIT = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYSUBUNIT), + KP_LEFTPAREN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTPAREN), + KP_RIGHTPAREN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTPAREN), + KP_LEFTBRACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTBRACE), + KP_RIGHTBRACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTBRACE), + KP_TAB = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_TAB), + KP_BACKSPACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BACKSPACE), + KP_A = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_A), + KP_B = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_B), + KP_C = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_C), + KP_D = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_D), + KP_E = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_E), + KP_F = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_F), + KP_XOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_XOR), + KP_POWER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_POWER), + KP_PERCENT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERCENT), + KP_LESS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LESS), + KP_GREATER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_GREATER), + KP_AMPERSAND = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AMPERSAND), + KP_DBLAMPERSAND = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLAMPERSAND), + KP_VERTICALBAR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_VERTICALBAR), + KP_DBLVERTICALBAR = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLVERTICALBAR), + KP_COLON = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COLON), + KP_HASH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HASH), + KP_SPACE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_SPACE), + KP_AT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AT), + KP_EXCLAM = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EXCLAM), + KP_MEMSTORE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSTORE), + KP_MEMRECALL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMRECALL), + KP_MEMCLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMCLEAR), + KP_MEMADD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMADD), + KP_MEMSUBTRACT = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSUBTRACT), + KP_MEMMULTIPLY = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMMULTIPLY), + KP_MEMDIVIDE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMDIVIDE), + KP_PLUSMINUS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUSMINUS), + KP_CLEAR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEAR), + KP_CLEARENTRY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEARENTRY), + KP_BINARY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BINARY), + KP_OCTAL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_OCTAL), + KP_DECIMAL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DECIMAL), + KP_HEXADECIMAL = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HEXADECIMAL), - LCTRL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LCTRL), - LSHIFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LSHIFT), - LALT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LALT), - LGUI = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LGUI), - RCTRL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RCTRL), - RSHIFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RSHIFT), - RALT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RALT), - RGUI = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RGUI), + LCTRL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LCTRL), + LSHIFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LSHIFT), + LALT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LALT), + LGUI = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LGUI), + RCTRL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RCTRL), + RSHIFT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RSHIFT), + RALT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RALT), + RGUI = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RGUI), - MODE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MODE), + MODE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MODE), - AUDIONEXT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIONEXT), - AUDIOPREV = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOPREV), - AUDIOSTOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOSTOP), - AUDIOPLAY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOPLAY), - AUDIOMUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOMUTE), - MEDIASELECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIASELECT), - WWW = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_WWW), - MAIL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MAIL), - CALCULATOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CALCULATOR), - COMPUTER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COMPUTER), - AC_SEARCH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_SEARCH), - AC_HOME = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_HOME), - AC_BACK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BACK), - AC_FORWARD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_FORWARD), - AC_STOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_STOP), - AC_REFRESH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_REFRESH), - AC_BOOKMARKS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BOOKMARKS), + AUDIONEXT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIONEXT), + AUDIOPREV = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOPREV), + AUDIOSTOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOSTOP), + AUDIOPLAY = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOPLAY), + AUDIOMUTE = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOMUTE), + MEDIASELECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIASELECT), + WWW = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_WWW), + MAIL = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MAIL), + CALCULATOR = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CALCULATOR), + COMPUTER = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COMPUTER), + AC_SEARCH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_SEARCH), + AC_HOME = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_HOME), + AC_BACK = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BACK), + AC_FORWARD = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_FORWARD), + AC_STOP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_STOP), + AC_REFRESH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_REFRESH), + AC_BOOKMARKS = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BOOKMARKS), - BRIGHTNESSDOWN = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_BRIGHTNESSDOWN), - BRIGHTNESSUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_BRIGHTNESSUP), - DISPLAYSWITCH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DISPLAYSWITCH), - KBDILLUMTOGGLE = - SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMTOGGLE), - KBDILLUMDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMDOWN), - KBDILLUMUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMUP), - EJECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EJECT), - SLEEP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SLEEP)*/ - } + BRIGHTNESSDOWN = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_BRIGHTNESSDOWN), + BRIGHTNESSUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_BRIGHTNESSUP), + DISPLAYSWITCH = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DISPLAYSWITCH), + KBDILLUMTOGGLE = + SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMTOGGLE), + KBDILLUMDOWN = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMDOWN), + KBDILLUMUP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KBDILLUMUP), + EJECT = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EJECT), + SLEEP = SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SLEEP)*/ } + // #define SCANCODE_MASK (1<<30) // #define SDL_SCANCODE_TO_KEYCODE(X) (X | SCANCODE_MASK) diff --git a/libs/SDL2/SDL.flx b/libs/SDL2/SDL.flx index d8403ab4..b5860924 100644 --- a/libs/SDL2/SDL.flx +++ b/libs/SDL2/SDL.flx @@ -2,137 +2,137 @@ // Copyright (c) 2014 - 2016, zhiayang // Licensed under the Apache License Version 2.0. +export SDL + import "Keyboard.flx" -public namespace SDL + +public let INIT_TIMER: u32 = 0x00000001 +public let INIT_AUDIO: u32 = 0x00000010 +public let INIT_VIDEO: u32 = 0x00000020 +public let INIT_JOYSTICK: u32 = 0x00000200 +public let INIT_HAPTIC: u32 = 0x00001000 +public let INIT_GAMECONTROLLER: u32 = 0x00002000 +public let INIT_EVENTS: u32 = 0x00004000 +public let INIT_NOPARACHUTE: u32 = 0x00100000 +public let INIT_EVERYTHING: u32 = INIT_TIMER | INIT_AUDIO | INIT_VIDEO | INIT_EVENTS | + INIT_JOYSTICK | INIT_HAPTIC | INIT_GAMECONTROLLER + +public let GL_RED_SIZE: i32 = 0 +public let GL_GREEN_SIZE: i32 = 1 +public let GL_BLUE_SIZE: i32 = 2 +public let GL_ALPHA_SIZE: i32 = 3 +public let GL_BUFFER_SIZE: i32 = 4 +public let GL_DOUBLEBUFFER: i32 = 5 +public let GL_DEPTH_SIZE: i32 = 6 +public let GL_STENCIL_SIZE: i32 = 7 +public let GL_ACCUM_RED_SIZE: i32 = 8 +public let GL_ACCUM_GREEN_SIZE: i32 = 9 +public let GL_ACCUM_BLUE_SIZE: i32 = 10 +public let GL_ACCUM_ALPHA_SIZE: i32 = 11 +public let GL_STEREO: i32 = 12 +public let GL_MULTISAMPLEBUFFERS: i32 = 13 +public let GL_MULTISAMPLESAMPLES: i32 = 14 +public let GL_ACCELERATED_VISUAL: i32 = 15 +public let GL_RETAINED_BACKING: i32 = 16 +public let GL_CONTEXT_MAJOR_VERSION: i32 = 17 +public let GL_CONTEXT_MINOR_VERSION: i32 = 18 +public let GL_CONTEXT_EGL: i32 = 19 +public let GL_CONTEXT_FLAGS: i32 = 20 +public let GL_CONTEXT_PROFILE_MASK: i32 = 21 +public let GL_SHARE_WITH_CURRENT_CONTEXT: i32 = 22 +public let GL_FRAMEBUFFER_SRGB_CAPABLE: i32 = 23 +public let GL_CONTEXT_RELEASE_BEHAVIOR: i32 = 24 + + +public enum EventType: u32 +{ + case FIRSTEVENT = 0 + + case Quit = 0x100 + case App_Terminating = 0x101 + case App_LowMemory = 0x102 + case App_WillEnterBackground = 0x103 + case App_DidEnterBackground = 0x104 + case App_WillEnterForeground = 0x105 + case App_DidEnterForeground = 0x106 + + case WindowEvent = 0x200 + case SysWMEvent = 0x201 + + case KeyDown = 0x300 + case KeyUp = 0x301 + case TextEditing = 0x302 + case TextInput = 0x303 + case KeymapChanged = 0x304 + + case Mouse_Motion = 0x400 + case Mouse_ButtonDown = 0x401 + case Mouse_ButtonUp = 0x402 + case Mouse_Wheel = 0x403 + + case Joy_AxisMotion = 0x600 + case Joy_BallMotion = 0x601 + case Joy_HatMotion = 0x602 + case Joy_ButtonDown = 0x603 + case Joy_ButtonUp = 0x604 + case Joy_DeviceAdded = 0x605 + case Joy_DeviceRemoved = 0x606 + + case Controller_AxisMotion = 0x650 + case Controller_ButtonDown = 0x651 + case Controller_ButtonUp = 0x652 + case Controller_DeviceAdded = 0x653 + case Controller_DeviceRemoved = 0x654 + case Controller_DeviceRemapped = 0x655 + + case FingerDown = 0x700 + case FingerUp = 0x701 + case FingerMotion = 0x702 + + case DollarGesture = 0x800 + case DollarRecord = 0x801 + case MultiGesture = 0x802 + + case ClipboardUpdate = 0x900 + + case DropFile = 0x1000 + + case Audio_DeviceAdded = 0x1100 + case Audio_DeviceRemoved = 0x1101 + + case Render_TargetsReset = 0x2000 + case Render_DeviceReset = 0x2001 + + case USEREVENT = 0x8000 + case LASTEVENT = 0xFFFF +} + +public struct Event +{ + type: EventType + lol: [i8: 52] +} + +struct KeySym { - public let INIT_TIMER: u32 = 0x00000001 - public let INIT_AUDIO: u32 = 0x00000010 - public let INIT_VIDEO: u32 = 0x00000020 - public let INIT_JOYSTICK: u32 = 0x00000200 - public let INIT_HAPTIC: u32 = 0x00001000 - public let INIT_GAMECONTROLLER: u32 = 0x00002000 - public let INIT_EVENTS: u32 = 0x00004000 - public let INIT_NOPARACHUTE: u32 = 0x00100000 - public let INIT_EVERYTHING: u32 = SDL.INIT_TIMER | SDL.INIT_AUDIO | SDL.INIT_VIDEO | SDL.INIT_EVENTS | - SDL.INIT_JOYSTICK | SDL.INIT_HAPTIC | SDL.INIT_GAMECONTROLLER - - public let GL_RED_SIZE: i32 = 0 - public let GL_GREEN_SIZE: i32 = 1 - public let GL_BLUE_SIZE: i32 = 2 - public let GL_ALPHA_SIZE: i32 = 3 - public let GL_BUFFER_SIZE: i32 = 4 - public let GL_DOUBLEBUFFER: i32 = 5 - public let GL_DEPTH_SIZE: i32 = 6 - public let GL_STENCIL_SIZE: i32 = 7 - public let GL_ACCUM_RED_SIZE: i32 = 8 - public let GL_ACCUM_GREEN_SIZE: i32 = 9 - public let GL_ACCUM_BLUE_SIZE: i32 = 10 - public let GL_ACCUM_ALPHA_SIZE: i32 = 11 - public let GL_STEREO: i32 = 12 - public let GL_MULTISAMPLEBUFFERS: i32 = 13 - public let GL_MULTISAMPLESAMPLES: i32 = 14 - public let GL_ACCELERATED_VISUAL: i32 = 15 - public let GL_RETAINED_BACKING: i32 = 16 - public let GL_CONTEXT_MAJOR_VERSION: i32 = 17 - public let GL_CONTEXT_MINOR_VERSION: i32 = 18 - public let GL_CONTEXT_EGL: i32 = 19 - public let GL_CONTEXT_FLAGS: i32 = 20 - public let GL_CONTEXT_PROFILE_MASK: i32 = 21 - public let GL_SHARE_WITH_CURRENT_CONTEXT: i32 = 22 - public let GL_FRAMEBUFFER_SRGB_CAPABLE: i32 = 23 - public let GL_CONTEXT_RELEASE_BEHAVIOR: i32 = 24 - - - public enum EventType: u32 - { - case FIRSTEVENT = 0 - - case Quit = 0x100 - case App_Terminating = 0x101 - case App_LowMemory = 0x102 - case App_WillEnterBackground = 0x103 - case App_DidEnterBackground = 0x104 - case App_WillEnterForeground = 0x105 - case App_DidEnterForeground = 0x106 - - case WindowEvent = 0x200 - case SysWMEvent = 0x201 - - case KeyDown = 0x300 - case KeyUp = 0x301 - case TextEditing = 0x302 - case TextInput = 0x303 - case KeymapChanged = 0x304 - - case Mouse_Motion = 0x400 - case Mouse_ButtonDown = 0x401 - case Mouse_ButtonUp = 0x402 - case Mouse_Wheel = 0x403 - - case Joy_AxisMotion = 0x600 - case Joy_BallMotion = 0x601 - case Joy_HatMotion = 0x602 - case Joy_ButtonDown = 0x603 - case Joy_ButtonUp = 0x604 - case Joy_DeviceAdded = 0x605 - case Joy_DeviceRemoved = 0x606 - - case Controller_AxisMotion = 0x650 - case Controller_ButtonDown = 0x651 - case Controller_ButtonUp = 0x652 - case Controller_DeviceAdded = 0x653 - case Controller_DeviceRemoved = 0x654 - case Controller_DeviceRemapped = 0x655 - - case FingerDown = 0x700 - case FingerUp = 0x701 - case FingerMotion = 0x702 - - case DollarGesture = 0x800 - case DollarRecord = 0x801 - case MultiGesture = 0x802 - - case ClipboardUpdate = 0x900 - - case DropFile = 0x1000 - - case Audio_DeviceAdded = 0x1100 - case Audio_DeviceRemoved = 0x1101 - - case Render_TargetsReset = 0x2000 - case Render_DeviceReset = 0x2001 - - case USEREVENT = 0x8000 - case LASTEVENT = 0xFFFF - } - - public struct Event - { - type: SDL::EventType - lol: [i8: 52] - } - - public class KeyboardEvent - { - public struct KeySym - { - // SDL_Scancode scancode - scancode: u32 - sym: SDL::Key - - mod: u16 - unused: u32 - } - - var type: SDL::EventType - var timestamp: u32 - var windowid: u32 - var state: u8 - var repeat: u8 - var padding: u16 - var keysym: SDL::KeyboardEvent::KeySym - } + // SDL_Scancode scancode + scancode: u32 + sym: Keyboard::Key + + mod: u16 + unused: u32 +} + +public struct KeyboardEvent +{ + type: EventType + timestamp: u32 + windowid: u32 + state: u8 + repeat: u8 + padding: u16 + keysym: KeySym } public ffi fn SDL_Init(flags: u32) -> i32 @@ -147,3 +147,7 @@ public ffi fn SDL_PollEvent(event: &SDL::Event) -> i32 public ffi fn SDL_GL_CreateContext(window: &void) -> &void public ffi fn SDL_GL_SwapWindow(window: &void) -> void + + + + diff --git a/source/fir/Types/EnumType.cpp b/source/fir/Types/EnumType.cpp index 3d85c620..1154529c 100644 --- a/source/fir/Types/EnumType.cpp +++ b/source/fir/Types/EnumType.cpp @@ -55,16 +55,24 @@ namespace fir } - void EnumType::setNameArray(fir::ConstantValue* arr) + void EnumType::setNameArray(ConstantValue* arr) { this->runtimeNameArray = arr; } - void EnumType::setCaseArray(fir::ConstantValue* arr) + void EnumType::setCaseArray(ConstantValue* arr) { this->runtimeCasesArray = arr; } + void EnumType::setCaseType(Type* t) + { + if(!this->caseType->isVoidType()) + error("cannot modify enum type! was previously '%s'", this->caseType); + + this->caseType = t; + } + @@ -79,16 +87,6 @@ namespace fir return (typeCache[name] = new EnumType(name, caseType)); } - EnumType* EnumType::getEmpty() - { - static EnumType* empty = 0; - - if(!empty) - empty = get(Identifier("", IdKind::Name), Type::getVoid()); - - return empty; - } - fir::Type* EnumType::substitutePlaceholders(const util::hash_map& subst) { if(this->containsPlaceholders()) diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index 233a00ce..9082176a 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -33,11 +33,6 @@ namespace parser // first one { - bool hadParen = false; - - if(st.front() == TT::LParen) - hadParen = true, st.eat(); - // ok -- the first one we must do manually. // probably not, but i'm lazy. while(st.front() == TT::Val || st.front() == TT::Var) @@ -53,13 +48,6 @@ namespace parser // ok, we finished doing the variables. cases.back().cond = parseExpr(st); - if(hadParen) - { - if(st.front() != TT::RParen) - expected(st, "closing parenthesis ')'", st.front().str()); - - st.eat(); - } if(auto x = parseBracedBlock(st); x.isError()) return PResult::copyError(x); @@ -73,7 +61,6 @@ namespace parser while(st.frontAfterWS() == TT::Else) { st.eat(); - bool paren = false; if(st.front() == TT::If) { @@ -81,8 +68,6 @@ namespace parser // ok, it's an else-if. st.eat(); - if(st.front() == TT::LParen) - paren = true, st.eat(); while(st.front() == TT::Val || st.front() == TT::Var) { @@ -96,14 +81,6 @@ namespace parser } c.cond = parseExpr(st); - if(paren) - { - if(st.front() != TT::RParen) - expected(st, "closing parenthesis ')'", st.front().str()); - - st.eat(); - } - if(auto x = parseBracedBlock(st); x.isError()) return PResult::copyError(x); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index 98e400e0..77d125e6 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -461,6 +461,9 @@ namespace parser { st.skipWS(); + if(st.front() == TT::RBrace) + break; + if(st.eat() != TT::Case) expected(st.ploc(), "'case' inside enum body", st.prev().str()); diff --git a/source/include/ir/type.h b/source/include/ir/type.h index cdc1a962..cb21747f 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -848,6 +848,7 @@ namespace fir ConstantValue* getNameArray(); ConstantValue* getCaseArray(); + void setCaseType(Type* t); void setNameArray(ConstantValue* arr); void setCaseArray(ConstantValue* arr); @@ -870,7 +871,6 @@ namespace fir // static funcs public: static EnumType* get(const Identifier& name, Type* caseType); - static EnumType* getEmpty(); }; diff --git a/source/typecheck/arithmetic.cpp b/source/typecheck/arithmetic.cpp index 35dfd44f..1c3909fd 100644 --- a/source/typecheck/arithmetic.cpp +++ b/source/typecheck/arithmetic.cpp @@ -194,6 +194,11 @@ fir::Type* sst::TypecheckState::getBinaryOpResultType(fir::Type* left, fir::Type return left; } } + else if(util::match(op, Operator::BitwiseOr, Operator::BitwiseAnd, Operator::BitwiseXor)) + { + if(left == right) + return left; + } // ok, check the operator map. diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index 8c3d65e9..453373bc 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -43,7 +43,9 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->id.scope = this->realScope; defn->visibility = this->visibility; defn->original = this; - defn->type = fir::EnumType::getEmpty(); + + // set it to void first, because we want to defer typechecking the member type. + defn->type = fir::EnumType::get(defn->id, fir::Type::getVoid()); if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) return TCResult(err); @@ -74,7 +76,9 @@ TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con if(this->memberType) defn->memberType = fs->convertParserTypeToFIR(this->memberType); else defn->memberType = fir::Type::getNativeWord(); - auto ety = fir::EnumType::get(defn->id, defn->memberType); + auto ety = defn->type->toEnumType(); + iceAssert(ety); + ety->setCaseType(defn->memberType); size_t index = 0; for(auto cs : this->cases) diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 7b7862aa..266e1f02 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -279,7 +279,7 @@ namespace sst //* note: if our size is 1, we should check if s == toplevel_name -- if so, then we're declaring //* things in the global scope -- which is allowed! - if(s == tree->name) + if(i == 0 && s == tree->name) continue; if(auto it = tree->subtrees.find(s); it == tree->subtrees.end()) From 998abf6ad0f939a8aed410e188effc8c0029c08c Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 27 Nov 2019 23:02:01 +0800 Subject: [PATCH 103/129] Fix conversion warning --- source/include/zpr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/include/zpr.h b/source/include/zpr.h index 8885ba98..5dbd81c7 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -407,7 +407,7 @@ namespace zpr const char* fmt_str = 0; auto spec = args.specifier; - static std::map len_specs = { + static std::map len_specs = { { format_args::LENGTH_SHORT_SHORT, "hh" }, { format_args::LENGTH_SHORT, "h" }, { format_args::LENGTH_LONG, "l" }, From a39c3dfdc945e0429b185083395636e64c55752a Mon Sep 17 00:00:00 2001 From: zhiayang Date: Wed, 27 Nov 2019 23:48:12 +0800 Subject: [PATCH 104/129] Fix dumb bugs because I can't program C++. this fixes the build on windows. --- source/backend/llvm/linker.cpp | 4 +++- source/backend/llvm/translator.cpp | 2 +- source/include/zpr.h | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index c182bf42..87b29938 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -211,7 +211,9 @@ namespace backend if(frontend::getOutputMode() == ProgOutputMode::RunJit) { - const char* argv = ("llvm-jit-" + this->linkedModule->getModuleIdentifier()).c_str(); + std::string modname = ("llvm-jit-" + this->linkedModule->getModuleIdentifier()); + const char* argv = modname.c_str(); + auto entry = this->getEntryFunctionFromJIT(); _printTiming(ts, "llvm jit"); diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 9e91e805..fd7e0b2b 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -489,7 +489,7 @@ namespace backend ty = fir::Type::getString(); // add -1 for the capacity and 0 for the refcountptr. - mems.push_back(llvm::ConstantInt::get(getNativeWordTy(), -1)); + mems.push_back(llvm::ConstantInt::get(getNativeWordTy(), static_cast(-1))); mems.push_back(zconst); } diff --git a/source/include/zpr.h b/source/include/zpr.h index 5dbd81c7..5b362c07 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -404,7 +405,6 @@ namespace zpr char buf[65] = {0}; size_t digits_len = 0; - const char* fmt_str = 0; auto spec = args.specifier; static std::map len_specs = { @@ -417,8 +417,9 @@ namespace zpr { format_args::LENGTH_PTRDIFF_T, "t" } }; - fmt_str = ("%" + len_specs[args.length] + spec).c_str(); - digits_len = snprintf(&buf[0], 64, fmt_str, x); + auto fmt_str = ("%" + len_specs[args.length] + spec); + + digits_len = snprintf(&buf[0], 64, fmt_str.c_str(), x); // sadly, we must cheat here as well, because osx doesn't bloody have charconv (STILL)? From 7599513c73b313fc85b2e19e7b8a6fd486b9e9cb Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 28 Nov 2019 12:17:29 +0800 Subject: [PATCH 105/129] Make things deprecated in prep for refactor. --- build/tmp/repro_1.flx | 1 + makefile | 2 +- source/include/typecheck.h | 23 ++++++++++++++++++ source/typecheck/typecheckstate.cpp | 37 +++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index 39b3dac4..c0777d7e 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -10,3 +10,4 @@ import repro_2 as _ // printf("four = %d\n", repro_2::Foo::FOUR.value); printf("%d\n", Bar::bla(3)); } + diff --git a/makefile b/makefile index 374f42cb..a0f77a72 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ -WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wno-deprecated-declarations +WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wdeprecated-declarations GCCWARNINGS := -Wno-init-list-lifetime diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 6b7cb156..b2ad20ee 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -46,6 +46,19 @@ namespace sst struct Solution_t; } + struct Scope + { + Scope() { } + Scope(StateTree* st); + + StateTree* stree = 0; + + const Scope* prev = 0; + const Scope* next = 0; + + std::vector getStrings() const; + }; + struct StateTree { StateTree(const std::string& nm, const std::string& filename, StateTree* p, bool anon = false) @@ -84,6 +97,9 @@ namespace sst util::hash_map> prefixOperatorOverloads; util::hash_map> postfixOperatorOverloads; + Scope cachedScope; + const Scope& getScope2(); + std::vector getScope(); StateTree* searchForName(const std::string& name); @@ -178,10 +194,17 @@ namespace sst void pushAnonymousTree(); std::string serialiseCurrentScope(); + + [[deprecated]] std::vector getCurrentScope(); + + [[deprecated]] void teleportToScope(const std::vector& scope); + + [[deprecated]] StateTree* getTreeOfScope(const std::vector& scope); + std::vector getDefinitionsWithName(const std::string& name, StateTree* tree = 0); ErrorMsg* checkForShadowingOrConflictingDefinition(Defn* def, std::function checkConflicting, StateTree* tree = 0); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 266e1f02..c59cf375 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -346,6 +346,34 @@ namespace sst this->addDefinition(this->topLevelFilename, _name, def, gmaps); } + std::vector Scope::getStrings() const + { + std::vector ret; + const Scope* s = this; + while(s) + { + ret.push_back(s->stree->name); + s = s->prev; + } + + std::reverse(ret.begin(), ret.end()); + return ret; + } + + const Scope& StateTree::getScope2() + { + if(!this->cachedScope.stree) + { + this->cachedScope.stree = this; + + // all hail recursion. + if(this->parent) + this->cachedScope.prev = &this->parent->getScope2(); + } + + return this->cachedScope; + } + // TODO: maybe cache this someday? std::vector StateTree::getScope() { @@ -524,6 +552,15 @@ namespace sst static size_t _anonId = 0; this->pushTree(std::to_string(_anonId++), /* createAnonymously: */ true); } + + + Scope::Scope(StateTree* st) + { + this->next = 0; + this->prev = 0; + + this->stree = st; + } } From 20060d6637af4d5529f2707089d54b008f07d3ae Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 16 Jun 2020 12:22:43 +0800 Subject: [PATCH 106/129] update to llvm 10 --- source/backend/llvm/jit.cpp | 13 +++++++------ source/backend/llvm/linker.cpp | 7 +++---- source/include/backends/llvm.h | 2 ++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/source/backend/llvm/jit.cpp b/source/backend/llvm/jit.cpp index e2003b89..35e04e5e 100644 --- a/source/backend/llvm/jit.cpp +++ b/source/backend/llvm/jit.cpp @@ -41,15 +41,16 @@ static std::string dealWithLLVMError(const llvm::Error& err) namespace backend { LLVMJit::LLVMJit(llvm::orc::JITTargetMachineBuilder JTMB, llvm::DataLayout DL) : ObjectLayer(ES, []() { - return llvm::make_unique(); + return std::make_unique(); }), - CompileLayer(ES, ObjectLayer, llvm::orc::ConcurrentIRCompiler(std::move(JTMB))), + CompileLayer(ES, ObjectLayer, std::make_unique(std::move(JTMB))), OptimiseLayer(ES, CompileLayer, optimiseModule), DL(std::move(DL)), Mangle(ES, this->DL), - Ctx(llvm::make_unique()) + Ctx(std::make_unique()), + dylib(ES.createJITDylib("")) { llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); - ES.getMainJITDylib().setGenerator(llvm::cantFail( + dylib.addGenerator(llvm::cantFail( llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(DL.getGlobalPrefix()))); // dunno who's bright idea it was to match symbol flags *EXACTLY* instead of something more sane @@ -63,13 +64,13 @@ namespace backend auto modIdent = mod->getModuleIdentifier(); // llvm::Error::operator bool() returns true if there's an error. - if(auto err = OptimiseLayer.add(ES.getMainJITDylib(), llvm::orc::ThreadSafeModule(std::move(mod), Ctx)); err) + if(auto err = OptimiseLayer.add(this->dylib, llvm::orc::ThreadSafeModule(std::move(mod), Ctx)); err) error("llvm: failed to add module '%s': %s", modIdent, dealWithLLVMError(err)); } llvm::JITEvaluatedSymbol LLVMJit::findSymbol(const std::string& name) { - if(auto ret = ES.lookup({ &ES.getMainJITDylib() }, Mangle(name)); !ret) + if(auto ret = ES.lookup({ &this->dylib }, Mangle(name)); !ret) error("llvm: failed to find symbol '%s': %s", name, dealWithLLVMError(ret.takeError())); else diff --git a/source/backend/llvm/linker.cpp b/source/backend/llvm/linker.cpp index 87b29938..d010aee7 100644 --- a/source/backend/llvm/linker.cpp +++ b/source/backend/llvm/linker.cpp @@ -244,14 +244,13 @@ namespace backend llvm::SmallVector buffer; { - auto bufferStream = llvm::make_unique(buffer); + auto bufferStream = std::make_unique(buffer); llvm::raw_pwrite_stream* rawStream = bufferStream.get(); { llvm::legacy::PassManager pm = llvm::legacy::PassManager(); - - using CodeGenFileType = llvm::TargetMachine::CodeGenFileType; - targetMachine->addPassesToEmitFile(pm, *rawStream, nullptr, CodeGenFileType::CGFT_ObjectFile); + targetMachine->addPassesToEmitFile(pm, *rawStream, nullptr, + llvm::CodeGenFileType::CGFT_ObjectFile); pm.run(*this->linkedModule); } diff --git a/source/include/backends/llvm.h b/source/include/backends/llvm.h index 2905bf72..4a59546c 100644 --- a/source/include/backends/llvm.h +++ b/source/include/backends/llvm.h @@ -89,6 +89,8 @@ namespace backend llvm::DataLayout DL; llvm::orc::MangleAndInterner Mangle; llvm::orc::ThreadSafeContext Ctx; + + llvm::orc::JITDylib& dylib; }; struct LLVMBackend : Backend From 30eb26111e4e8b0d4953dbc1fb013fe2d05afd82 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 9 Nov 2020 18:06:13 +0800 Subject: [PATCH 107/129] uwu --- .gitignore | 1 + build/a.flx | 17 + build/b.flx | 7 + build/c.flx | 30 + build/d.flx | 6 + build/supertiny.flx | 1 + build/tmp/repro_1.flx | 5 +- libs/libc.flx | 106 ++-- libs/std/file.flx | 70 +++ libs/std/io.flx | 134 +---- libs/std/print.flx | 134 +++++ makefile | 4 +- repro_1 | Bin 0 -> 12700 bytes source/backend/llvm/linker.cpp | 2 +- source/backend/llvm/translator.cpp | 2 +- source/codegen/builtin.cpp | 2 +- source/codegen/call.cpp | 4 +- source/codegen/constructor.cpp | 4 +- source/codegen/glue/saa_common.cpp | 2 +- source/codegen/literals.cpp | 2 +- source/codegen/misc.cpp | 2 +- source/codegen/raii.cpp | 2 +- source/codegen/unions.cpp | 2 +- source/fir/ConstantValue.cpp | 4 +- source/fir/IRBuilder.cpp | 4 +- source/fir/Types/ClassType.cpp | 6 +- source/fir/Types/FunctionType.cpp | 2 +- source/fir/Types/StructType.cpp | 4 +- source/fir/Types/TupleType.cpp | 2 +- source/fir/Types/TypeUtils.cpp | 6 +- source/fir/interp/compiler.cpp | 4 +- source/frontend/import.cpp | 2 +- source/frontend/parser/controlflow.cpp | 2 +- source/frontend/parser/expr.cpp | 4 +- source/frontend/parser/function.cpp | 2 +- source/frontend/parser/toplevel.cpp | 2 +- source/include/defs.h | 2 - source/include/precompile.h | 3 + source/include/typecheck.h | 10 + source/include/utils.h | 326 ---------- source/include/zfu.h | 621 ++++++++++++++++++++ source/include/zpr.h | 57 +- source/include/ztmu.h | 180 +++++- source/repl/commands.cpp | 99 +--- source/repl/history.cpp | 2 +- source/typecheck/alloc.cpp | 2 +- source/typecheck/arithmetic.cpp | 4 +- source/typecheck/call.cpp | 8 +- source/typecheck/classes.cpp | 2 +- source/typecheck/controlflow.cpp | 2 +- source/typecheck/dotop.cpp | 18 +- source/typecheck/function.cpp | 8 +- source/typecheck/polymorph/driver.cpp | 8 +- source/typecheck/polymorph/instantiator.cpp | 9 +- source/typecheck/polymorph/misc.cpp | 4 +- source/typecheck/polymorph/solver.cpp | 18 +- source/typecheck/resolver/driver.cpp | 17 +- source/typecheck/resolver/misc.cpp | 12 +- source/typecheck/resolver/resolver.cpp | 22 +- source/typecheck/structs.cpp | 2 +- source/typecheck/toplevel.cpp | 47 +- source/typecheck/type.cpp | 3 +- source/typecheck/typecheckstate.cpp | 54 +- source/typecheck/using.cpp | 6 +- 64 files changed, 1390 insertions(+), 739 deletions(-) create mode 100644 build/a.flx create mode 100644 build/b.flx create mode 100644 build/c.flx create mode 100644 build/d.flx create mode 100644 libs/std/file.flx create mode 100644 libs/std/print.flx create mode 100755 repro_1 delete mode 100644 source/include/utils.h create mode 100644 source/include/zfu.h diff --git a/.gitignore b/.gitignore index 29a500b8..df09c0c7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ # Makefile dependencies *.cpp.d *.c.d +*.h.d # Compiled Dynamic libraries *.so diff --git a/build/a.flx b/build/a.flx new file mode 100644 index 00000000..d45d94dd --- /dev/null +++ b/build/a.flx @@ -0,0 +1,17 @@ +// a.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +// import libc as _ +export a +import libc + +import std::io as _ + +// import b +// import d + +@entry fn main() +{ + print("uwu\n") +} diff --git a/build/b.flx b/build/b.flx new file mode 100644 index 00000000..aaf12892 --- /dev/null +++ b/build/b.flx @@ -0,0 +1,7 @@ +// b.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +export a +import libc + diff --git a/build/c.flx b/build/c.flx new file mode 100644 index 00000000..0179f2d3 --- /dev/null +++ b/build/c.flx @@ -0,0 +1,30 @@ +// ultratiny.flx +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +export ultratiny + +// import libc as _ +import a + +@entry fn main() +{ + // printf("q = %d\n", 37) +} + + + + + + + + + + + + + + + + + diff --git a/build/d.flx b/build/d.flx new file mode 100644 index 00000000..4e2c8f01 --- /dev/null +++ b/build/d.flx @@ -0,0 +1,6 @@ +// d.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +export a +import libc diff --git a/build/supertiny.flx b/build/supertiny.flx index fcd316a4..f4a9b74d 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -5,6 +5,7 @@ export supertiny +import std::io import libc as _ // import std::io diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index c0777d7e..acca60ac 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -3,11 +3,14 @@ // Licensed under the Apache License Version 2.0. import libc as _ -import repro_2 as _ +import repro_2 + +using repro_2 as _ @entry fn main() { // printf("four = %d\n", repro_2::Foo::FOUR.value); printf("%d\n", Bar::bla(3)); + // printf("lmao\n"); } diff --git a/libs/libc.flx b/libs/libc.flx index 108e5714..e3e097a2 100644 --- a/libs/libc.flx +++ b/libs/libc.flx @@ -8,64 +8,74 @@ export libc public ffi fn puts(fmt: &i8) -> i32 public ffi fn printf(fmt: &i8, ...) -> i32 public ffi fn sprintf(fmt: &i8, y: &i8, ...) -> i32 -public ffi fn snprintf(fmt: &i8, l: u64, y: &i8, ...) -> i32 +// public ffi fn snprintf(fmt: &i8, l: u64, y: &i8, ...) -> i32 -public ffi fn fprintf(stream: &void, y: &i8, ...) -> i32 +// public ffi fn fprintf(stream: &void, y: &i8, ...) -> i32 -public ffi fn putchar(x: i32) -> i32 +// public ffi fn putchar(x: i32) -> i32 -// memcpy/set/move -public ffi fn memcpy(dest: &i8, source: &i8, length: u64) -> &i8 -public ffi fn memmove(dest: &i8, source: &i8, length: u64) -> &i8 -public ffi fn memset(dest: &i8, value: i32, length: u64) -> &i8 +// // memcpy/set/move +// public ffi fn memcpy(dest: &i8, source: &i8, length: u64) -> &i8 +// public ffi fn memmove(dest: &i8, source: &i8, length: u64) -> &i8 +// public ffi fn memset(dest: &i8, value: i32, length: u64) -> &i8 -// heap -// public ffi fn malloc(size: i64) -> &i8 -// public ffi fn free(pointer: &i8) +// // heap +// // public ffi fn malloc(size: i64) -> &i8 +// // public ffi fn free(pointer: &i8) -// strings -public ffi fn strlen(s: &i8) -> i64 -public ffi fn strcmp(s1: &i8, s2: &i8) -> i32 -public ffi fn strncmp(s1: &i8, s2: &i8, length: i64) -> i32 +// // strings +// public ffi fn strlen(s: &i8) -> i64 +// public ffi fn strcmp(s1: &i8, s2: &i8) -> i32 +// public ffi fn strncmp(s1: &i8, s2: &i8, length: i64) -> i32 -// random things -public ffi fn usleep(usec: u32) -> i32 -public ffi fn sleep(sec: u32) -> u32 +// // random things +// public ffi fn usleep(usec: u32) -> i32 +// public ffi fn sleep(sec: u32) -> u32 public ffi fn abort() -public ffi fn exit(status: i32) - -// file stuff -public ffi fn fsync(fd: i32) -> i32 -public ffi fn fflush(fd: &void) -> i32 -public ffi fn ioctl(fd: i32, cmd: u64, ...) -> i32 - -public ffi fn fopen(path: &i8, mode: &i8) -> &void -public ffi fn fread(buf: &i8, sz: u64, cnt: u64, file: &void) -> u64 -public ffi fn fclose(file: &void) -> i32 - -// unistd.h - - -// posix stuff, that windows likes to rename for some reason!!!!! -#if os::name == "windows" -{ - // unistd.h - public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 as "_read" - public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 as "_write" - - // stdio.h - public ffi fn fdopen(fd: i32, mode: &i8) -> &void as "_fdopen" -} -else -{ - // unistd.h - public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 +// public ffi fn exit(status: i32) + +// // file stuff +// public ffi fn fsync(fd: i32) -> i32 +// public ffi fn fflush(fd: &void) -> i32 +// public ffi fn ioctl(fd: i32, cmd: u64, ...) -> i32 + +// public ffi fn fopen(path: &i8, mode: &i8) -> &void +// public ffi fn fread(buf: &i8, sz: u64, cnt: u64, file: &void) -> u64 +// public ffi fn fclose(file: &void) -> i32 + +// // unistd.h + + +// // posix stuff, that windows likes to rename for some reason!!!!! +// #if os::name == "windows" +// { +// // unistd.h +// public ffi fn open(path: &i8, flags: i32, mode: i32) -> i32 as "_open" +// public ffi fn close(fd: i32) -> i32 as "_close" + +// public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 as "_read" +// public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 as "_write" + +// public ffi fn lseek(fd: i32, ofs: i64, whence: i32) -> i64 as "_lseek" + +// // stdio.h +// public ffi fn fdopen(fd: i32, mode: &i8) -> &void as "_fdopen" +// } +// else +// { +// // unistd.h +// public ffi fn open(path: &i8, flags: i32, mode: i32) -> i32 +// public ffi fn close(fd: i32) -> i32 + +// public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 - // stdio.h - public ffi fn fdopen(fd: i32, mode: &i8) -> &void -} +// public ffi fn lseek(fd: i32, ofs: i64, whence: i32) -> i64 + +// // stdio.h +// public ffi fn fdopen(fd: i32, mode: &i8) -> &void +// } diff --git a/libs/std/file.flx b/libs/std/file.flx new file mode 100644 index 00000000..01b7f7e7 --- /dev/null +++ b/libs/std/file.flx @@ -0,0 +1,70 @@ +// file.flx +// Copyright (c) 2019, zhiayang +// Licensed under the Apache License Version 2.0. + +export std::io +import libc + +// // rudimentary file handling +// public struct File +// { +// fd: int +// buf: [i8] +// } + +// public enum Mode +// { +// case Read +// case Write +// case ReadWrite +// case Append +// } + +// let O_RDONLY: i32 = 0x0000 // open for reading only +// let O_WRONLY: i32 = 0x0001 // open for writing only +// let O_RDWR: i32 = 0x0002 // open for reading and writing +// let O_ACCMODE: i32 = 0x0003 // mask for above modes +// let O_NONBLOCK: i32 = 0x0004 // no delay +// let O_APPEND: i32 = 0x0008 // set append mode +// let O_CREAT: i32 = 0x0200 // create if nonexistant +// let O_TRUNC: i32 = 0x0400 // truncate to zero length +// let O_EXCL: i32 = 0x0800 // error if already exists + +// let SEEK_SET: i32 = 0 // set file offset to offset +// let SEEK_CUR: i32 = 1 // set file offset to current plus offset +// let SEEK_END: i32 = 2 // set file offset to EOF plus offset + +// public fn open(path: str, mode: Mode) -> File +// { +// var flags: i32 +// if mode == Mode::Read => flags = O_RDONLY +// if mode == Mode::Write => flags = O_WRONLY +// if mode == Mode::ReadWrite => flags = O_RDWR +// if mode == Mode::Append => flags = O_RDWR | O_APPEND + +// return File(fd: libc::open(path, flags, 0), buf: [ ]) +// } + +// public fn read(f: File) -> [i8] +// { +// if f.buf.length != 0 => return f.buf + + +// return [ ] +// } + + + + + + + + + + + + + + + + diff --git a/libs/std/io.flx b/libs/std/io.flx index 04829967..1952c13a 100644 --- a/libs/std/io.flx +++ b/libs/std/io.flx @@ -1,134 +1,8 @@ -// stdio.flx -// Copyright (c) 2017, zhiayang +// io.flx +// Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. export std::io -import libc - -fn error(msg: str) -{ - libc::printf("invalid format string: '%s'\n", msg) - libc::abort() -} - -fn to_string_i64(n: i64) -> string -{ - var ret = @raw alloc i8 [16] - let len = libc::sprintf(ret, "%lld", n) - - let s = string(ret, len) - free ret - - return s -} - -fn to_string_u64(n: u64) -> string -{ - var ret = @raw alloc i8 [16] - let len = libc::sprintf(ret, "%llu", n) - - let s = string(ret, len) - free ret - - return s -} - - -fn to_string_f64(n: f64) -> string -{ - var ret = @raw alloc i8 [24] - let len = libc::sprintf(ret, "%f", n) - - let s = string(ret, len) - free ret - - return s -} - - - -public fn format(fmt: str, args: [any: ...]) -> string -{ - // todo: this is quite inefficient. - // should we make some kind of stringbuilder class? - - var ret: string - var argi = 0 - - var idx = 0 - while idx < fmt.length - { - let ch = fmt[idx] - if ch == '%' - { - if argi >= args.length - { - error(format("too few arguments: got only %, expected at least %", args.length, argi + 1)) - } - else - { - let arg = args[argi] - argi += 1 - - if arg is u8 => ret.append(to_string_u64(arg as u8)) - else if arg is u16 => ret.append(to_string_u64(arg as u16)) - else if arg is u32 => ret.append(to_string_u64(arg as u32)) - else if arg is u64 => ret.append(to_string_u64(arg as u64)) - - else if arg is i8 => ret.append(to_string_i64(arg as i8)) - else if arg is i16 => ret.append(to_string_i64(arg as i16)) - else if arg is i32 => ret.append(to_string_i64(arg as i32)) - else if arg is i64 => ret.append(to_string_i64(arg as i64)) - - else if arg is f32 => ret.append(to_string_f64(arg as f32)) - else if arg is f64 => ret.append(to_string_f64(arg as f64)) - - else if arg is string => ret.append(arg as string) - else if arg is str => ret.append(arg as str) - - else => ret.append("(?)") - } - } - else - { - if ch == '\\' - { - idx += 1 - ret.append(fmt[idx]) - } - else - { - ret.append(ch) - } - } - - idx += 1 - } - - return ret -} - -// in case you're lazy -public fn println() -{ - libc::puts("") -} - -public fn println(fmt: str, args: [any: ...]) -{ - libc::puts(format(fmt, ...args)) -} - -public fn print(fmt: str, args: [any: ...]) -{ - let s = format(fmt, ...args) - libc::write(1, s.ptr, s.length) -} - - - - - - - +public import file +public import print diff --git a/libs/std/print.flx b/libs/std/print.flx new file mode 100644 index 00000000..c85431cb --- /dev/null +++ b/libs/std/print.flx @@ -0,0 +1,134 @@ +// print.flx +// Copyright (c) 2017, zhiayang +// Licensed under the Apache License Version 2.0. + +export std::io +import libc + +fn error(msg: str) +{ + libc::printf("invalid format string: '%s'\n", msg) + libc::abort() +} + +fn to_string_i64(n: i64) -> string +{ + var ret = @raw alloc i8 [16] + let len = libc::sprintf(ret, "%lld", n) + + let s = string(ret, len) + free ret + + return s +} + +fn to_string_u64(n: u64) -> string +{ + var ret = @raw alloc i8 [16] + let len = libc::sprintf(ret, "%llu", n) + + let s = string(ret, len) + free ret + + return s +} + + +fn to_string_f64(n: f64) -> string +{ + var ret = @raw alloc i8 [24] + let len = libc::sprintf(ret, "%f", n) + + let s = string(ret, len) + free ret + + return s +} + + + +public fn format(fmt: str, args: [any: ...]) -> string +{ + // todo: this is quite inefficient. + // should we make some kind of stringbuilder class? + + var ret: string + var argi = 0 + + var idx = 0 + while idx < fmt.length + { + let ch = fmt[idx] + if ch == '%' + { + if argi >= args.length + { + error(format("too few arguments: got only %, expected at least %", args.length, argi + 1)) + } + else + { + let arg = args[argi] + argi += 1 + + if arg is u8 => ret.append(to_string_u64(arg as u8)) + else if arg is u16 => ret.append(to_string_u64(arg as u16)) + else if arg is u32 => ret.append(to_string_u64(arg as u32)) + else if arg is u64 => ret.append(to_string_u64(arg as u64)) + + else if arg is i8 => ret.append(to_string_i64(arg as i8)) + else if arg is i16 => ret.append(to_string_i64(arg as i16)) + else if arg is i32 => ret.append(to_string_i64(arg as i32)) + else if arg is i64 => ret.append(to_string_i64(arg as i64)) + + else if arg is f32 => ret.append(to_string_f64(arg as f32)) + else if arg is f64 => ret.append(to_string_f64(arg as f64)) + + else if arg is string => ret.append(arg as string) + else if arg is str => ret.append(arg as str) + + else => ret.append("(?)") + } + } + else + { + if ch == '\\' + { + idx += 1 + ret.append(fmt[idx]) + } + else + { + ret.append(ch) + } + } + + idx += 1 + } + + return ret +} + +// in case you're lazy +public fn println() +{ + libc::puts("") +} + +public fn println(fmt: str, args: [any: ...]) +{ + libc::puts(format(fmt, ...args)) +} + +public fn print(fmt: str, args: [any: ...]) +{ + let s = format(fmt, ...args) + libc::write(1, s.ptr, s.length) +} + + + + + + + + diff --git a/makefile b/makefile index a0f77a72..0a973048 100644 --- a/makefile +++ b/makefile @@ -100,7 +100,7 @@ TESTSRC := build/tester.flx .DEFAULT_GOAL = jit -include $(CXXDEPS) - +-include source/include/precompile.h.d .PHONY: copylibs jit compile clean build linux ci satest tiny @@ -157,7 +157,7 @@ $(OUTPUT): $(PRECOMP_GCH) $(CXXOBJ) $(COBJ) $(UTF8REWIND_AR) %.h.gch: %.h @printf "# precompiling header $<\n" - @$(CXX) $(CXXFLAGS) $(WARNINGS) -o $@ $< + @$(CXX) $(CXXFLAGS) $(WARNINGS) -o $@ $< -MMD -MP $(UTF8REWIND_AR): diff --git a/repro_1 b/repro_1 new file mode 100755 index 0000000000000000000000000000000000000000..ef072862080d2dc91190d0582572fcf9a6a74dc0 GIT binary patch literal 12700 zcmeHN&ud&&6uxg7+i4A%DRxnRL>>*1v`UgTrF3B-6O%ZhrA?C{t((J4=A{{!A7SQA zOV4RJ>raE z0g;utJ1hM|?0hbq6z3xqLp|r6_eBbiKF``=!gKN|RAjcW zQjqs<-CT}N3(iB|If>7UYl%ta7950KpRxl}<7vHB{&xM)S9yH3!b+YkO zdBL}m$}8qjq*jvlR=wP;$wt^Z6mLH-;3f0IXSMHrh*U~pDO!@xbbeVs$}h&FpTy&w zr1U=THx_PQpPOBX*f(Y}wNLnz&mBo@oN4>d8MAGN;K8-CXwTY6^zh;5VNRl5L`!}c ze+ZxB&s>C!_GKvJ6y_Z(-%rdbV`<4^{)_gBuLI*l2cZ+4c59+mEl*UoY8CjD*P+bg z`rYqd`Sz=8-h=UP{`~Bdhn-94^PzT+xl?8UO53h+{GH4N*m-CyW7xmlFV1^Xxb9Ub zW6a;Hc=&qrdbuE<<*jxY)W>JWkyo{hL6Eng#3!lb=e+aDmx?l=3@8K2fHI&AC-6zE9j{c5|*Nw-+ zf1oKWcSnCg=gvAqPyWJ;(aI^9ybHd;K1j0V9gn|3FQcW7xceY&E;@+k+2|IT0lw#> z4>!XM?uY}pBMzLgJ^~*v8E-T1cd!`Op!&#w!aHG77<(6*kHTGhZ%qzRTNzLW zlmTTx8Bhk40cAiLPzIC%Wk4BF2968^SKa?-kIaU3qso9XpbRJj%78MU3@8K2fHI&A zCh`G&epPptBtSyF@GU~%oynef7c1#-)*=SW8;Tk-MC#c8Ih{@MW)rMIx znfkf}o55-)3{dr6;>79s`Bt#eYRaXlnNn-2jEd*^f=pGfTsE_;hd5?-hw<2c6ZY4CNYE}g@0WAehqy?C~A-E+wapqiP~2`s9d}gwbg8WR8b$k1(Ab07E4CV(*Y($WaM70SuWL3c3%x;t auto { + auto tolink = zfu::map(frontend::getLibrariesToLink(), [](auto lib) -> auto { return platform::compiler::getSharedLibraryName(lib); }) + platform::compiler::getDefaultSharedLibraries(); diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index fd7e0b2b..d9f346ce 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -139,7 +139,7 @@ namespace backend // to allow recursion, declare the type first. createdTypes[ct->getTypeName()] = llvm::StructType::create(gc, ct->getTypeName().mangled()); - std::vector lmems = util::map(ct->getAllElementsIncludingBase(), [&mod](auto t) -> auto { + std::vector lmems = zfu::map(ct->getAllElementsIncludingBase(), [&mod](auto t) -> auto { return typeToLlvm(t, mod); }); diff --git a/source/codegen/builtin.cpp b/source/codegen/builtin.cpp index 2ad06d54..d244d255 100644 --- a/source/codegen/builtin.cpp +++ b/source/codegen/builtin.cpp @@ -47,7 +47,7 @@ CGResult sst::BuiltinDotOp::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->isFunctionCall) { - std::vector arguments = util::map(this->args, [cs](sst::Expr* e) -> fir::Value* { return e->codegen(cs).value; }); + std::vector arguments = zfu::map(this->args, [cs](sst::Expr* e) -> fir::Value* { return e->codegen(cs).value; }); if(this->name == names::saa::FN_CLONE) { iceAssert(arguments.empty()); diff --git a/source/codegen/call.cpp b/source/codegen/call.cpp index 21321ddc..9a157a0e 100644 --- a/source/codegen/call.cpp +++ b/source/codegen/call.cpp @@ -228,7 +228,7 @@ std::vector cgn::CodegenState::codegenAndArrangeFunctionCallArgumen { idxmap = this->getNameIndexMap(fd); - util::foreachIdx(fd->params, [&defaultArgs](const FnParam& arg, size_t idx) { + zfu::foreachIdx(fd->params, [&defaultArgs](const FnParam& arg, size_t idx) { if(arg.defaultVal) defaultArgs[idx] = arg.defaultVal; }); @@ -437,7 +437,7 @@ CGResult sst::ExprCall::_codegen(cgn::CodegenState* cs, fir::Type* infer) } } - std::vector fcas = util::map(this->arguments, [](sst::Expr* arg) -> FnCallArgument { + std::vector fcas = zfu::map(this->arguments, [](sst::Expr* arg) -> FnCallArgument { return FnCallArgument(arg->loc, "", arg, /* orig: */ nullptr); }); diff --git a/source/codegen/constructor.cpp b/source/codegen/constructor.cpp index f22650e4..534931b3 100644 --- a/source/codegen/constructor.cpp +++ b/source/codegen/constructor.cpp @@ -89,7 +89,7 @@ fir::Value* cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, { auto restore = this->irb.getCurrentBlock(); - auto arglist = util::map(vargs, [](fir::Value* v) -> auto { + auto arglist = zfu::map(vargs, [](fir::Value* v) -> auto { return v->getType(); }); @@ -101,7 +101,7 @@ fir::Value* cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, // make the self: auto selfptr = this->irb.StackAlloc(cls, "self"); - std::vector argvals = util::map(wrapper_func->getArguments(), [](auto a) -> fir::Value* { + std::vector argvals = zfu::map(wrapper_func->getArguments(), [](auto a) -> fir::Value* { return a; }); diff --git a/source/codegen/glue/saa_common.cpp b/source/codegen/glue/saa_common.cpp index ea912558..57b3638c 100644 --- a/source/codegen/glue/saa_common.cpp +++ b/source/codegen/glue/saa_common.cpp @@ -335,7 +335,7 @@ namespace saa_common if(appendee == getSAAElm(saa)) return generateElementAppendFunction(cs, saa); - else if(util::match(appendee, getSAASlice(saa), getSAASlice(saa, false), saa)) + else if(zfu::match(appendee, getSAASlice(saa), getSAASlice(saa, false), saa)) return generateAppendFunction(cs, saa); else diff --git a/source/codegen/literals.cpp b/source/codegen/literals.cpp index 0169523e..32b7899f 100644 --- a/source/codegen/literals.cpp +++ b/source/codegen/literals.cpp @@ -215,7 +215,7 @@ CGResult sst::LiteralTuple::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(allConst) { - std::vector consts = util::map(vals, [](auto e) -> auto { return dcast(fir::ConstantValue, e); }); + std::vector consts = zfu::map(vals, [](auto e) -> auto { return dcast(fir::ConstantValue, e); }); return CGResult(fir::ConstantTuple::get(consts)); } else diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index dd8dcb59..2837de09 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -12,7 +12,7 @@ CGResult sst::TypeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) CGResult sst::ScopeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) { - error(this, "failed to resolve scope '%s'", util::serialiseScope(this->scope)); + error(this, "failed to resolve scope '%s'", zfu::join(this->scope, "::")); } CGResult sst::TreeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) diff --git a/source/codegen/raii.cpp b/source/codegen/raii.cpp index d0a17c97..5a9b6dc0 100644 --- a/source/codegen/raii.cpp +++ b/source/codegen/raii.cpp @@ -310,7 +310,7 @@ namespace cgn auto def = dcast(sst::StructDefn, cs->typeDefnMap[str]); iceAssert(def); - return util::matchAny(def->traits, [name](sst::TraitDefn* trt) -> bool { + return zfu::matchAny(def->traits, [name](sst::TraitDefn* trt) -> bool { // if we do not have such an attribute, then ::get returns an empty UA, // with an empty string as a name. return trt->attrs.get(strs::attrs::COMPILER_SUPPORT).name == strs::attrs::COMPILER_SUPPORT; diff --git a/source/codegen/unions.cpp b/source/codegen/unions.cpp index ca43dbdc..b80e46b9 100644 --- a/source/codegen/unions.cpp +++ b/source/codegen/unions.cpp @@ -48,7 +48,7 @@ CGResult sst::UnionVariantConstructor::_codegen(cgn::CodegenState* cs, fir::Type } else { - auto tupt = fir::TupleType::get(util::map(this->args, [](const FnCallArgument& fca) -> fir::Type* { + auto tupt = fir::TupleType::get(zfu::map(this->args, [](const FnCallArgument& fca) -> fir::Type* { return fca.value->type; })); diff --git a/source/fir/ConstantValue.cpp b/source/fir/ConstantValue.cpp index 2b21f711..99ca5efa 100644 --- a/source/fir/ConstantValue.cpp +++ b/source/fir/ConstantValue.cpp @@ -370,7 +370,7 @@ namespace fir std::string ConstantTuple::str() { - return "(" + util::join(util::map(this->values, [](auto x) -> auto { return x->str(); }), ", ") + ")"; + return "(" + zfu::join(zfu::map(this->values, [](auto x) -> auto { return x->str(); }), ", ") + ")"; } @@ -445,7 +445,7 @@ namespace fir std::string ConstantArray::str() { - return "[ " + util::join(util::map(this->values, [](auto x) -> auto { return x->str(); }), ", ") + " ]"; + return "[ " + zfu::join(zfu::map(this->values, [](auto x) -> auto { return x->str(); }), ", ") + " ]"; } diff --git a/source/fir/IRBuilder.cpp b/source/fir/IRBuilder.cpp index 6de411d6..3c9cfab6 100644 --- a/source/fir/IRBuilder.cpp +++ b/source/fir/IRBuilder.cpp @@ -425,7 +425,7 @@ namespace fir { iceAssert(v->getType()->isFloatingPointType() && targetType->isFloatingPointType() && "not floating point type"); Instruction* instr = make_instr(OpKind::Floating_Truncate, false, targetType, - util::vectorOf(v, ConstantValue::getZeroValue(targetType)) + zfu::vectorOf(v, ConstantValue::getZeroValue(targetType)) ); return this->addInstruction(instr, vname); @@ -1106,7 +1106,7 @@ namespace fir iceAssert(self && self == cls); Instruction* instr = make_instr(OpKind::Value_CallVirtualMethod, true, ft->getReturnType(), - util::vectorOf( + zfu::vectorOf( ConstantValue::getZeroValue(cls), ConstantInt::getNative(index), ConstantValue::getZeroValue(ft) diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 21b0cbb3..3eb4f666 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -279,7 +279,7 @@ namespace fir void ClassType::addTraitImpl(TraitType* trt) { - if(util::contains(this->implTraits, trt)) + if(zfu::contains(this->implTraits, trt)) error("'%s' already implements trait '%s'", this, trt); this->implTraits.push_back(trt); @@ -287,7 +287,7 @@ namespace fir bool ClassType::implementsTrait(TraitType* trt) { - return util::contains(this->implTraits, trt); + return zfu::contains(this->implTraits, trt); } std::vector ClassType::getImplementedTraits() @@ -323,7 +323,7 @@ namespace fir //* but if we do override something, we just set the method in our 'reverse' map, which is what we'll use to build //* the vtable. simple? - auto list = util::drop(method->getType()->toFunctionType()->getArgumentTypes(), 1); + auto list = zfu::drop(method->getType()->toFunctionType()->getArgumentTypes(), 1); // check every member of the current mapping -- not the fastest method i admit. bool found = false; diff --git a/source/fir/Types/FunctionType.cpp b/source/fir/Types/FunctionType.cpp index bd6ce939..e63b8fa9 100644 --- a/source/fir/Types/FunctionType.cpp +++ b/source/fir/Types/FunctionType.cpp @@ -134,7 +134,7 @@ namespace fir fir::Type* FunctionType::substitutePlaceholders(const util::hash_map& subst) { - auto args = util::map(this->functionParams, [&subst](auto t) -> auto { return t->substitutePlaceholders(subst); }); + auto args = zfu::map(this->functionParams, [&subst](auto t) -> auto { return t->substitutePlaceholders(subst); }); auto ret = this->functionRetType->substitutePlaceholders(subst); if(this->isFnCStyleVarArg) return FunctionType::getCVariadicFunc(args, ret); diff --git a/source/fir/Types/StructType.cpp b/source/fir/Types/StructType.cpp index 0fe99ad8..520ad047 100644 --- a/source/fir/Types/StructType.cpp +++ b/source/fir/Types/StructType.cpp @@ -105,7 +105,7 @@ namespace fir void StructType::addTraitImpl(TraitType* trt) { - if(util::contains(this->implTraits, trt)) + if(zfu::contains(this->implTraits, trt)) error("'%s' already implements trait '%s'", this, trt); this->implTraits.push_back(trt); @@ -113,7 +113,7 @@ namespace fir bool StructType::implementsTrait(TraitType* trt) { - return util::contains(this->implTraits, trt); + return zfu::contains(this->implTraits, trt); } std::vector StructType::getImplementedTraits() diff --git a/source/fir/Types/TupleType.cpp b/source/fir/Types/TupleType.cpp index 5b2180bc..35a7148c 100644 --- a/source/fir/Types/TupleType.cpp +++ b/source/fir/Types/TupleType.cpp @@ -62,7 +62,7 @@ namespace fir fir::Type* TupleType::substitutePlaceholders(const util::hash_map& subst) { - auto args = util::map(this->members, [&subst](auto t) -> auto { return t->substitutePlaceholders(subst); }); + auto args = zfu::map(this->members, [&subst](auto t) -> auto { return t->substitutePlaceholders(subst); }); return TupleType::get(members); } diff --git a/source/fir/Types/TypeUtils.cpp b/source/fir/Types/TypeUtils.cpp index 4faf1d32..e455ac16 100644 --- a/source/fir/Types/TypeUtils.cpp +++ b/source/fir/Types/TypeUtils.cpp @@ -79,7 +79,7 @@ namespace fir return false; // drop the first argument. - for(auto [ b, d ] : util::zip(base, derv)) + for(auto [ b, d ] : zfu::zip(base, derv)) { if(!areTypesContravariant(b, d, traitChecking)) return false; @@ -90,8 +90,8 @@ namespace fir bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn, bool traitChecking) { - bool ret = areTypeListsContravariant(util::drop(base->getArgumentTypes(), 1), - util::drop(fn->getArgumentTypes(), 1), traitChecking); + bool ret = areTypeListsContravariant(zfu::drop(base->getArgumentTypes(), 1), + zfu::drop(fn->getArgumentTypes(), 1), traitChecking); if(!ret) return false; diff --git a/source/fir/interp/compiler.cpp b/source/fir/interp/compiler.cpp index b856d2e9..83f60276 100644 --- a/source/fir/interp/compiler.cpp +++ b/source/fir/interp/compiler.cpp @@ -33,7 +33,7 @@ namespace interp interp::Block ret; ret.blk = fib; - ret.instructions = util::map(fib->getInstructions(), [is, parent](fir::Instruction* i) -> interp::Instruction { + ret.instructions = zfu::map(fib->getInstructions(), [is, parent](fir::Instruction* i) -> interp::Instruction { return compileInstruction(is, parent, i); }); @@ -47,7 +47,7 @@ namespace interp interp::Function ret; ret.func = fn; - ret.blocks = util::map(fn->getBlockList(), [fn, this](fir::IRBlock* b) -> interp::Block { + ret.blocks = zfu::map(fn->getBlockList(), [fn, this](fir::IRBlock* b) -> interp::Block { return compileBlock(this, fn, b); }); diff --git a/source/frontend/import.cpp b/source/frontend/import.cpp index 145e3575..121c5735 100644 --- a/source/frontend/import.cpp +++ b/source/frontend/import.cpp @@ -107,7 +107,7 @@ namespace parser std::vector bits = parseIdentPath(tokens, &i); //* we concatanate the thing, using '/' as the path separator, and appending '.flx' to the end. - name = util::join(bits, "/") + ".flx"; + name = zfu::join(bits, "/") + ".flx"; } else { diff --git a/source/frontend/parser/controlflow.cpp b/source/frontend/parser/controlflow.cpp index 9082176a..7fe55c51 100644 --- a/source/frontend/parser/controlflow.cpp +++ b/source/frontend/parser/controlflow.cpp @@ -160,7 +160,7 @@ namespace parser iceAssert(st.front() == TT::For); st.eat(); - if(!util::match(st.front(), TT::Identifier, TT::LParen)) + if(!zfu::match(st.front(), TT::Identifier, TT::LParen)) expectedAfter(st.loc(), "'(' or identifier", "'for'", st.front().str()); auto ret = util::pool(st.ploc()); diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index f6bea076..3cdb883e 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -630,7 +630,7 @@ namespace parser Expr* parseCaretOrColonScopeExpr(State& st) { - iceAssert(util::match(st.front(), TT::DoubleColon, TT::Caret)); + iceAssert(zfu::match(st.front(), TT::DoubleColon, TT::Caret)); auto str = st.front().str(); auto loc = st.loc(); @@ -868,7 +868,7 @@ namespace parser static Expr* parseIdentifier(State& st) { - iceAssert(util::match(st.front(), TT::Identifier, TT::UnicodeSymbol)); + iceAssert(zfu::match(st.front(), TT::Identifier, TT::UnicodeSymbol)); std::string name = st.pop().str(); auto ident = util::pool(st.ploc(), name); diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index db90c8a8..e0449ca8 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -191,7 +191,7 @@ namespace parser ffn->returnType = defn->returnType; // make sure we don't have optional arguments here - util::foreach(ffn->params, [](const auto& a) { + zfu::foreach(ffn->params, [](const auto& a) { if(a.defaultValue) error(a.loc, "foreign functions cannot have optional arguments"); }); diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index a83136ad..3a45311e 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -91,7 +91,7 @@ namespace parser if(path.empty()) path = { frontend::removeExtensionFromFilename(frontend::getFilenameFromPath(fullname)) }; - return { path.back(), util::take(path, path.size() - 1) }; + return { path.back(), zfu::take(path, path.size() - 1) }; } diff --git a/source/include/defs.h b/source/include/defs.h index 35f1baeb..ab967c62 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -9,8 +9,6 @@ #include #include -#include "zpr.h" -#include "utils.h" #include "container.h" struct Identifier; diff --git a/source/include/precompile.h b/source/include/precompile.h index 4323da31..329a694e 100644 --- a/source/include/precompile.h +++ b/source/include/precompile.h @@ -33,6 +33,9 @@ #include #include +#include "zpr.h" +#include "zfu.h" + #endif diff --git a/source/include/typecheck.h b/source/include/typecheck.h index b2ad20ee..bc22bacf 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -76,6 +76,11 @@ namespace sst // there to be scopes where there are no braces! bool isAnonymous = false; + // this flag allows us to skip definitions that are generated by the compiler. every module + // needs these definitions to be generated, but they should *not* be imported into other modules + // like normal things. + bool isCompilerGenerated = false; + util::hash_map subtrees; util::hash_map> unresolvedGenericDefs; util::hash_map>, sst::Defn*> resolvedGenericDefs; @@ -110,6 +115,11 @@ namespace sst void addDefinition(const std::string& name, Defn* def, const TypeParamMap_t& gmaps = { }); void addDefinition(const std::string& sourceFile, const std::string& name, Defn* def, const TypeParamMap_t& gmaps = { }); + + + + // TODO: remove this! for debugging only. + void dump(); }; struct DefinitionTree diff --git a/source/include/utils.h b/source/include/utils.h deleted file mode 100644 index 166962aa..00000000 --- a/source/include/utils.h +++ /dev/null @@ -1,326 +0,0 @@ -// utils.h -// Copyright (c) 2017, zhiayang -// Licensed under the Apache License Version 2.0. - -#pragma once -#include -#include -#include -#include - -template -std::vector operator + (const std::vector& vec, const T& elm) -{ - auto copy = vec; - - copy.push_back(elm); - return copy; -} - -template -std::vector operator + (const T& elm, const std::vector& vec) -{ - auto copy = vec; - - copy.insert(copy.begin(), elm); - return copy; -} - -template -std::vector operator + (const std::vector& a, const std::vector& b) -{ - auto ret = a; - ret.insert(ret.end(), b.begin(), b.end()); - - return ret; -} - - -namespace util -{ - template - bool match(const T&) - { - return true; - } - - template - bool match(const T& first, const U& second) - { - return (first == second); - } - - template - bool match(const T& first, const U& second, const Args&... comps) - { - return (first == second) || match(first, comps...); - } - - template - std::vector merge(const std::vector& x, const Args&... xs) - { - return (x + ... + xs); - } - - - - template - std::vector vectorOf(const T& x) - { - return std::vector({ x }); - } - - template - std::vector vectorOf(const T& x, const Args&... xs) - { - return x + vectorOf(xs...); - } - - - template - U foldl(const U& i, const std::vector& xs, FoldOp fn) - { - auto ret = i; - for(const auto& x : xs) - ret = fn(ret, x); - - return ret; - } - - - - template ::type> - std::vector map(const std::vector& input, UnaryOp fn) - { - std::vector ret; ret.reserve(input.size()); - for(const auto& i : input) - ret.push_back(fn(i)); - - return ret; - } - - template - void foreach(const std::vector& input, UnaryOp fn) - { - for(const auto& i : input) - fn(i); - } - - template - void foreachIdx(const std::vector& input, UnaryOp fn) - { - for(size_t i = 0; i < input.size(); i++) - fn(input[i], i); - } - - - template ::type> - std::vector mapidx(const std::vector& input, UnaryOp fn) - { - std::vector ret; ret.reserve(input.size()); - for(size_t i = 0; i < input.size(); i++) - ret.push_back(fn(input[i], i)); - - return ret; - } - - - - template ::type> - std::vector filterMap(const std::vector& input, Predicate cond, UnaryOp fn) - { - std::vector ret; - for(const auto& i : input) - { - if(cond(i)) - ret.push_back(fn(i)); - } - - return ret; - } - - template ::type> - std::vector mapFilter(const std::vector& input, UnaryOp fn, Predicate cond) - { - std::vector ret; - for(const auto& i : input) - { - auto k = fn(i); - if(cond(k)) ret.push_back(k); - } - - return ret; - } - - template - std::vector filter(const std::vector& input, Predicate cond) - { - std::vector ret; - for(const auto& i : input) - if(cond(i)) - ret.push_back(i); - - return ret; - } - - template - std::vector filterUntil(const std::vector& input, Predicate cond) - { - std::vector ret; - for(const auto& i : input) - { - if(cond(i)) ret.push_back(i); - else break; - } - - return ret; - } - - template - bool matchAny(const std::vector& input, Predicate cond) - { - for(const auto& x : input) - if(cond(x)) return true; - - return false; - } - - template - bool matchNone(const std::vector& input, Predicate cond) - { - return !matchAny(input, cond); - } - - template - bool matchAll(const std::vector& input, Predicate cond) - { - for(const auto& x : input) - if(!cond(x)) return false; - - return true; - } - - template - size_t indexOf(const std::vector& input, Predicate cond) - { - for(size_t i = 0; i < input.size(); i++) - if(cond(input[i])) return i; - - return -1; - } - - template - bool contains(const std::vector& input, const T& x) - { - return std::find(input.begin(), input.end(), x) != input.end(); - } - - template - std::vector take(const std::vector& v, size_t num) - { - return std::vector(v.begin(), v.begin() + std::min(num, v.size())); - } - - template - std::vector drop(const std::vector& v, size_t num) - { - return std::vector(v.begin() + std::min(num, v.size()), v.end()); - } - - template - std::vector> cartesian(const std::vector& a, const std::vector& b) - { - std::vector> ret; - - for(size_t i = 0; i < a.size(); i++) - for(size_t k = 0; k < b.size(); k++) - ret.push_back({ a[i], b[k] }); - - return ret; - } - - template - std::vector> zip(const std::vector& a, const std::vector& b) - { - std::vector> ret; - for(size_t i = 0; i < std::min(a.size(), b.size()); i++) - ret.push_back({ a[i], b[i] }); - - return ret; - } - - inline std::string join(const std::vector& list, const std::string& sep) - { - if(list.empty()) return ""; - else if(list.size() == 1) return list[0]; - - std::string ret; - for(size_t i = 0; i < list.size() - 1; i++) - ret += list[i] + sep; - - return ret + list.back(); - } - - - - - - inline std::string serialiseScope(const std::vector& scope) - { - if(scope.empty()) return ""; - - std::string ret = scope[0]; - for(size_t i = 1; i < scope.size(); i++) - ret += "::" + scope[i]; - - return ret; - } - - inline std::string plural(const std::string& thing, size_t count) - { - return thing + (count == 1 ? "" : "s"); - } - - template - std::string listToString(const std::vector& list, UnaryOp fn) - { - std::string ret; - for(size_t i = 0; i < list.size(); i++) - { - ret += fn(list[i]); - if(i != list.size() - 1) - ret += ", "; - } - - return "[ " + ret + " ]"; - } - - template - std::vector> pairs(const std::unordered_map& map) - { - auto ret = std::vector>(map.begin(), map.end()); - return ret; - } - - template - std::vector> pairs(const std::map& map) - { - auto ret = std::vector>(map.begin(), map.end()); - return ret; - } -} - - - - - - - - - - - - - - - diff --git a/source/include/zfu.h b/source/include/zfu.h new file mode 100644 index 00000000..538ca253 --- /dev/null +++ b/source/include/zfu.h @@ -0,0 +1,621 @@ +// zfu.h +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +// updated 10/06/2020 +// origins: +// flax -- post 16/06/2020 +// ikurabot -- pre 16/06/2020 + +#pragma once +#include +#include +#include +#include +#include +#include + +template +std::vector operator + (const std::vector& vec, const T& elm) +{ + auto copy = vec; + + copy.push_back(elm); + return copy; +} + +template +std::vector operator + (const T& elm, const std::vector& vec) +{ + auto copy = vec; + + copy.insert(copy.begin(), elm); + return copy; +} + +template +std::vector operator + (const std::vector& a, const std::vector& b) +{ + auto ret = a; + ret.insert(ret.end(), b.begin(), b.end()); + + return ret; +} + + +template +std::vector& operator += (std::vector& vec, const T& elm) +{ + vec.push_back(elm); + return vec; +} + +template +std::vector& operator += (std::vector& vec, const std::vector& xs) +{ + vec.insert(vec.end(), xs.begin(), xs.end()); + return vec; +} + + +namespace zfu +{ + template + bool match(const T&) + { + return true; + } + + template + bool match(const T& first, const U& second) + { + return (first == second); + } + + template + bool match(const T& first, const U& second, const Args&... comps) + { + return (first == second) || match(first, comps...); + } + + template + std::vector merge(const std::vector& x, const Args&... xs) + { + return (x + ... + xs); + } + + + + template + std::vector vectorOf(const T& x) + { + return std::vector({ x }); + } + + template + std::vector vectorOf(const T& x, const Args&... xs) + { + return x + vectorOf(xs...); + } + + template >> + std::vector rangeOpen(const T& begin, const T& end, const T& step = 1) + { + std::vector ret; + ret.reserve((end - begin + 1) / step); + + T x = begin; + while(x != end) + { + ret.push_back(x); + x += step; + } + + return ret; + } + + template >> + std::vector rangeClosed(const T& begin, const T& end, const T& step = 1) + { + return rangeOpen(begin, end + 1, step); + } + + template + std::vector iterateWhile(const T& seed, Predicate pred, UnaryOp fn) + { + T x = seed; + std::vector ret; + + while(pred(x)) + { + ret.push_back(x); + x = fn(x); + } + + return ret; + } + + + + template + U foldl(const U& i, const Container& xs, FoldOp fn) + { + auto ret = i; + for(const auto& x : xs) + ret = fn(ret, x); + + return ret; + } + + template + T sum(const Container& xs) + { + return foldl(T(), xs, [](const T& a, const T& b) -> T { return a + b; }); + } + + + + template + auto map(const Container& input, UnaryOp fn) -> std::vector()))> + { + std::vector()))> ret; + ret.reserve(input.size()); + + for(const auto& i : input) + ret.push_back(fn(i)); + + return ret; + } + + template + auto map(Container&& input, UnaryOp fn) -> std::vector())))> + { + std::vector())))> ret; + ret.reserve(input.size()); + + for(auto& i : input) + ret.push_back(fn(std::move(i))); + + return ret; + } + + template + auto flatmap(const Container& input, UnaryOp fn) -> std::vector()))> + { + std::vector()))> ret; + ret.reserve(input.size()); + + for(const auto& i : input) + { + auto x = fn(i); + ret.insert(ret.end(), x.begin(), x.end()); + } + + return ret; + } + + + + + + + template + void foreach(const Container& input, UnaryOp fn) + { + for(const auto& i : input) + fn(i); + } + + + template + void foreachWhile(const Container& input, UnaryOp fn) + { + for(const auto& i : input) + if(!fn(i)) break; + } + + template + void foreachIdx(const Container& input, UnaryOp fn) + { + for(size_t i = 0; i < input.size(); i++) + fn(input[i], i); + } + + + template + auto mapIdx(const Container& input, UnaryOp fn) -> std::vector(), static_cast(0)))> + { + std::vector(), static_cast(0)))> ret; + ret.reserve(input.size()); + + for(size_t i = 0; i < input.size(); i++) + ret.push_back(fn(input[i], i)); + + return ret; + } + + + + template + auto filterMap(const Container& input, Predicate cond, UnaryOp fn) -> std::vector()))> + { + std::vector()))> ret; + for(const auto& i : input) + if(cond(i)) ret.push_back(fn(i)); + + return ret; + } + + template + auto mapFilter(const Container& input, UnaryOp fn, Predicate cond) -> std::vector()))> + { + std::vector()))> ret; + for(const auto& i : input) + { + auto k = fn(i); + if(cond(k)) ret.push_back(k); + } + + return ret; + } + + template + Container filter(const Container& input, Predicate cond) + { + Container ret; + for(const auto& i : input) + if(cond(i)) + ret.push_back(i); + + return ret; + } + + template + bool matchAny(const Container& input, Predicate cond) + { + for(const auto& x : input) + if(cond(x)) return true; + + return false; + } + + template + bool matchNone(const Container& input, Predicate cond) + { + return !matchAny(input, cond); + } + + template + bool matchAll(const Container& input, Predicate cond) + { + for(const auto& x : input) + if(!cond(x)) return false; + + return true; + } + + template + size_t indexOf(const Container& input, Predicate cond) + { + for(size_t i = 0; i < input.size(); i++) + if(cond(input[i])) return i; + + return -1; + } + + template + bool contains(const Container& input, const U& x) + { + return std::find(input.begin(), input.end(), x) != input.end(); + } + + template + std::vector take(const std::vector& v, size_t num) + { + return std::vector(v.begin(), v.begin() + std::min(num, v.size())); + } + + template + std::vector takeWhile(const std::vector& input, Predicate cond) + { + std::vector ret; + for(const auto& i : input) + { + if(cond(i)) ret.push_back(i); + else break; + } + + return ret; + } + + template + std::vector drop(const std::vector& v, size_t num) + { + return std::vector(v.begin() + std::min(num, v.size()), v.end()); + } + + template + std::vector dropWhile(const std::vector& input, Predicate cond) + { + bool flag = false; + std::vector ret; + + for(const auto& i : input) + { + if(!flag && cond(i)) continue; + else flag = true; + + ret.push_back(i); + } + + return ret; + } + + + + template ())> + std::map> groupBy(const std::vector& xs, GroupFn gfn) + { + std::map> groups; + for(const T& x : xs) + groups[gfn(x)].push_back(x); + + return groups; + } + + // special case for one to many. so cartesian(1, { 1, 2, 3, 4 }) gives (1, 1), (1, 2), (1, 3), ... + template + std::vector> cartesian(const T& a, const std::vector& xs) + { + return map(xs, [&a](const U& x) -> auto { + return std::make_pair(a, x); + }); + } + + // special case for two vectors, because tuples are a major pain in the ass. pairs >>> tuples. + template + std::vector> cartesian(const std::vector& a, const std::vector& b) + { + std::vector> ret; + + for(size_t i = 0; i < a.size(); i++) + for(size_t k = 0; k < b.size(); k++) + ret.push_back({ a[i], b[k] }); + + return ret; + } + + template + inline void cross_imp(F f) { f(); } + + template + inline void cross_imp(F f, const std::vector& h, const std::vector&... t) + { + for(const H& hs : h) + cross_imp([&](const Ts&... ts) { f(hs, ts...); }, t...); + } + + template + std::vector> cartesian(const std::vector&... in) + { + std::vector> res; + + cross_imp([&](Ts const&... ts) { + res.emplace_back(ts...); + }, in...); + + return res; + } + + + + + template + std::vector> _permutations(const std::vector& xs, size_t r) + { + if(r == 0) return { }; + + auto fact = [](size_t x) -> size_t { + size_t ret = 1; + while(x > 1) + ret *= x, x -= 1; + + return ret; + }; + + std::vector> ret; + if(permute_inside) ret.reserve(fact(xs.size()) / fact(xs.size() - r)); + else ret.reserve(fact(xs.size()) / (fact(r) * fact(xs.size() - r))); + + + std::function)> recurse; + + recurse = [&recurse, &xs, &ret](size_t R, size_t r, size_t i, std::vector cur) { + if(r == R) + { + if(permute_inside) + { + do { + ret.push_back(cur); + } while(std::next_permutation(cur.begin(), cur.end())); + } + else + { + std::sort(cur.begin(), cur.end()); + ret.push_back(cur); + } + } + else + { + for(size_t k = i; k < xs.size(); k++) + { + cur.push_back(xs[k]); + recurse(R, r + 1, k + 1, cur); + cur.pop_back(); + } + } + }; + + recurse(r, 0, 0, { }); + return ret; + } + + template + std::vector> powerset(const std::vector& xs) + { + std::vector> ret; + + // well... if there's more than 64 elements then gg + ret.reserve(2 << xs.size()); + + for(size_t i = 0; i < xs.size(); i++) + { + auto x = _permutations(xs, i); + ret.insert(ret.end(), x.begin(), x.end()); + } + + return ret; + } + + template + std::vector> combinations(const std::vector& xs, size_t r) + { + return _permutations(xs, r); + } + + template + std::vector> permutations(const std::vector& xs, size_t r) + { + return _permutations(xs, r); + } + + template + std::vector> permutations(const std::vector& xs) + { + return _permutations(xs, xs.size()); + } + + + + + + template + std::vector> zip(const std::vector& a, const std::vector& b) + { + std::vector> ret; + for(size_t i = 0; i < std::min(a.size(), b.size()); i++) + ret.push_back({ a[i], b[i] }); + + return ret; + } + + static inline std::string join(const std::vector& list, const std::string& sep) + { + if(list.empty()) return ""; + else if(list.size() == 1) return list[0]; + + std::string ret; + for(size_t i = 0; i < list.size() - 1; i++) + ret += list[i] + sep; + + return ret + list.back(); + } + + + + + + static inline std::string serialiseScope(const std::vector& scope) + { + if(scope.empty()) return ""; + + std::string ret = scope[0]; + for(size_t i = 1; i < scope.size(); i++) + ret += "::" + scope[i]; + + return ret; + } + + static inline std::string plural(const std::string& thing, size_t count) + { + return thing + (count == 1 ? "" : "s"); + } + + template + std::string listToString(const Container& list, UnaryOp fn, bool braces = true, const std::string& sep = ", ") + { + std::string ret; + for(size_t i = 0; i < list.size(); i++) + { + ret += fn(list[i]); + if(i != list.size() - 1) + ret += sep; + } + + return braces ? ("[ " + ret + " ]") : ret; + } + + template + std::vector> pairs(const std::unordered_map& map) + { + auto ret = std::vector>(map.begin(), map.end()); + return ret; + } + + template + std::vector> pairs(const std::map& map) + { + auto ret = std::vector>(map.begin(), map.end()); + return ret; + } + + + struct identity + { + template + T&& operator() (T&& x) { return std::forward(x); } + }; + + struct tostring + { + template + std::string operator() (const T& x) { return std::to_string(x); } + }; + + struct pair_first + { + template + T operator() (const std::pair& x) { return x.first; } + }; + + struct pair_second + { + template + U operator() (const std::pair& x) { return x.second; } + }; + + template + struct equals_to + { + equals_to(const T& x) : value(x) { } + bool operator() (const T& x) { return x == this->value; } + + private: + const T& value; + }; +} + + + + + + + + + + + + + + + diff --git a/source/include/zpr.h b/source/include/zpr.h index 5b362c07..b1682978 100644 --- a/source/include/zpr.h +++ b/source/include/zpr.h @@ -2,16 +2,20 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. +// updated 12/07/2020 +// origins: +// flax -- 12/07/2020 +// ikurabot -- 10/06/2020 + #pragma once +#include #include #include -#include #include -#include -#include #include +#include #ifndef ENABLE_FIELD_SIZES #define ENABLE_FIELD_SIZES 1 @@ -175,7 +179,11 @@ namespace zpr inline std::string sprint(const char* &fmt) { - return std::string(fmt); + if(!fmt || !*fmt) + return ""; + + auto tmp = skip(fmt, &fmt); + return tmp + sprint(fmt); } // we need to forward declare this. @@ -388,6 +396,7 @@ namespace zpr // else if(args.specifier == 'o') base = 8; // else if(args.specifier == 'b') base = 2; + #if 0 // handle negative values ourselves btw, due to padding bool is_neg = false; @@ -398,6 +407,7 @@ namespace zpr if(is_neg) x = -x; } + #endif std::string digits; { @@ -407,17 +417,30 @@ namespace zpr size_t digits_len = 0; auto spec = args.specifier; - static std::map len_specs = { - { format_args::LENGTH_SHORT_SHORT, "hh" }, - { format_args::LENGTH_SHORT, "h" }, - { format_args::LENGTH_LONG, "l" }, - { format_args::LENGTH_LONG_LONG, "ll" }, - { format_args::LENGTH_INTMAX_T, "j" }, - { format_args::LENGTH_SIZE_T, "z" }, - { format_args::LENGTH_PTRDIFF_T, "t" } + static const char* len_specs[] = { + /* LENGTH_DEFAULT */ "", + /* LENGTH_SHORT_SHORT */ "hh", + /* LENGTH_SHORT */ "h", + /* LENGTH_LONG */ "l", + /* LENGTH_LONG_LONG */ "ll", + /* LENGTH_LONG_DOUBLE */ "L", + /* LENGTH_INTMAX_T */ "j", + /* LENGTH_SIZE_T */ "z", + /* LENGTH_PTRDIFF_T */ "t", }; - auto fmt_str = ("%" + len_specs[args.length] + spec); + auto len_spec = len_specs[args.length]; + if(std::is_same_v>>) + len_spec = "ll"; + + if(std::is_same_v>>) + len_spec = "z"; + + if(std::is_same_v>>) + len_spec = "ll"; + + + auto fmt_str = ("%" + std::string(len_spec) + spec); digits_len = snprintf(&buf[0], 64, fmt_str.c_str(), x); @@ -442,8 +465,12 @@ namespace zpr } std::string prefix; + #if 0 if(is_neg) prefix += "-"; - else if(args.prepend_plus_if_positive) prefix += "+"; + else + #endif + + if(args.prepend_plus_if_positive) prefix += "+"; else if(args.prepend_blank_if_positive) prefix += " "; // prepend 0x or 0b or 0o for alternate. @@ -574,7 +601,7 @@ namespace zpr int64_t string_length = 0; int64_t abs_field_width = std::abs(args.width); - if constexpr (std::is_pointer_v>) + if constexpr (std::is_pointer_v>>) { for(int64_t i = 0; (args.precision != -1 ? (i < args.precision && x && x[i]) : (x && x[i])); i++) string_length++; diff --git a/source/include/ztmu.h b/source/include/ztmu.h index ce7c9df0..6ffb6779 100644 --- a/source/include/ztmu.h +++ b/source/include/ztmu.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,11 @@ namespace ztmu size_t getTerminalWidth(); size_t displayedTextLength(const std::string_view& str); + + inline std::vector prettyFormatTextBlock(const std::vector& paragraphs, + const char* leftMargin, const char* rightMargin, size_t maxLines = 0); + std::vector prettyFormatTextBlock(const std::string& block, const char* leftMargin, + const char* rightMargin, size_t maxLines = 0); } @@ -167,6 +173,8 @@ namespace detail void delete_right(State* st); size_t convertCursorToByteCursor(const char* bytes, size_t cursor); + std::vector pretty_print_text_block(const std::vector& paragraphs, const char* leftMargin, + const char* rightMargin, size_t maxLines); @@ -184,7 +192,7 @@ namespace detail -#if ZTMU_CREATE_IMPL // || true +#if ZTMU_CREATE_IMPL // comes as a pair yo #include "zpr.h" @@ -1700,7 +1708,7 @@ namespace detail } else { - delete_left(st); + delete_right(st); } } break; @@ -1940,6 +1948,125 @@ namespace detail return eof; } + std::vector pretty_print_text_block(const std::vector& paragraphs, const char* leftMargin, + const char* rightMargin, size_t maxLines) + { + auto split_words = [](const std::string& s) -> std::vector { + std::vector ret; + + size_t word_start = 0; + for(size_t i = 0; i < s.size(); i++) + { + if(s[i] == ' ') + { + ret.push_back(std::string_view(s.c_str() + word_start, i - word_start)); + word_start = i + 1; + } + else if(s[i] == '-') + { + ret.push_back(std::string_view(s.c_str() + word_start, i - word_start + 1)); + word_start = i + 1; + } + } + + ret.push_back(std::string_view(s.c_str() + word_start)); + return ret; + }; + + auto tw = ztmu::getTerminalWidth(); + tw = std::min(tw, tw - strlen(leftMargin) - strlen(rightMargin)); + + auto disp_len = ztmu::displayedTextLength; + + std::vector output; + + size_t lines = 1; + size_t paras = 0; + for(auto& l : paragraphs) + { + size_t remaining = tw; + + // sighs. + auto ss = std::stringstream(); + ss << leftMargin; + + // each "line" is actually a paragraph. we want to be nice, so pad on the right by a few spaces + // and hyphenate split words. + + // first split into words + auto words = split_words(l); + for(const auto& word : words) + { + if(maxLines > 0 && lines == maxLines && remaining <= word.size() + 4) + { + // don't. just quit. + ss << "..."; + break; + } + + auto len = disp_len(word); + if(remaining >= len) + { + ss << word << (word.back() != '-' ? " " : ""); + + remaining -= len; + + if(remaining > 0) + { + remaining -= 1; + } + else + { + ss << "\n" << leftMargin; + lines++; + + remaining = tw; + } + } + else if(remaining < 3 || len < 5) + { + // for anything less than 5 chars, put it on the next line -- don't hyphenate. + ss << "\n" << leftMargin << word << (word.back() != '-' ? " " : ""); + + remaining = tw - (len + 1); + lines++; + } + else + { + auto thisline = remaining - 2; + + // if we end up making a fragment 3 letters or shorter, + // push it to the next line instead. + if(std::min(word.size(), thisline ) <= 3) + { + thisline = 0; + ss << "\n" << leftMargin << word << (word.back() != '-' ? " " : ""); + } + else + { + // split it. + ss << word.substr(0, thisline) << "-" << "\n"; + ss << leftMargin << word.substr(thisline) << " "; + } + + remaining = tw - word.substr(thisline).size(); + + lines++; + } + } + + output.push_back(ss.str()); + + // if the paragraph has some space remaining at the end, then + // don't print the next one, just stop here. + if(maxLines > 0 && lines == maxLines && paras < paragraphs.size()) + break; + + paras++; + } + + return output; + } } } #endif @@ -1948,6 +2075,55 @@ namespace detail namespace ztmu { + inline std::vector prettyFormatTextBlock(const std::vector& paragraphs, const char* leftMargin, + const char* rightMargin, size_t maxLines) + { + return detail::pretty_print_text_block(paragraphs, leftMargin, rightMargin, maxLines); + } + + inline std::vector prettyFormatTextBlock(const std::string& block, const char* leftMargin, + const char* rightMargin, size_t maxLines) + { + auto splitString = [](std::string_view view, char delim = '\n') -> std::vector { + + std::vector ret; + + while(true) + { + size_t ln = view.find(delim); + + if(ln != std::string_view::npos) + { + ret.emplace_back(view.data(), ln); + view.remove_prefix(ln + 1); + } + else + { + break; + } + } + + // account for the case when there's no trailing newline, and we still have some stuff stuck in the view. + if(!view.empty()) + ret.emplace_back(view.data(), view.length()); + + return ret; + }; + + std::vector paragraphs; + auto splits = splitString(block); + for(auto sv : splits) + { + auto s = std::string(sv); + s.erase(std::remove(s.begin(), s.end(), '\r')); + + if(!s.empty()) + paragraphs.push_back(s); + } + + return detail::pretty_print_text_block(paragraphs, leftMargin, rightMargin, maxLines); + } + inline size_t displayedTextLength(const std::string_view& str) { return detail::displayedTextLength(str); diff --git a/source/repl/commands.cpp b/source/repl/commands.cpp index dca49d6d..e80450be 100644 --- a/source/repl/commands.cpp +++ b/source/repl/commands.cpp @@ -107,96 +107,15 @@ namespace repl static void print_help() { - auto split_words = [](std::string& s) -> std::vector { - std::vector ret; - - size_t word_start = 0; - for(size_t i = 0; i < s.size(); i++) - { - if(s[i] == ' ') - { - ret.push_back(std::string_view(s.c_str() + word_start, i - word_start)); - word_start = i + 1; - } - else if(s[i] == '-') - { - ret.push_back(std::string_view(s.c_str() + word_start, i - word_start + 1)); - word_start = i + 1; - } - } - - ret.push_back(std::string_view(s.c_str() + word_start)); - return ret; - }; - - constexpr const char* LEFT_MARGIN = " "; - constexpr const char* RIGHT_MARGIN = " "; - - auto tw = ztmu::getTerminalWidth(); - tw = std::min(tw, tw - strlen(LEFT_MARGIN) - strlen(RIGHT_MARGIN)); - - auto disp_len = ztmu::displayedTextLength; - - for(auto& l : helpLines) - { - size_t remaining = tw; - - // sighs. - auto ss = std::stringstream(); - ss << LEFT_MARGIN; - - // each "line" is actually a paragraph. we want to be nice, so pad on the right by a few spaces - // and hyphenate split words. - - // first split into words - auto words = split_words(l); - for(const auto& word : words) - { - // printf("w: '%s' -- ", std::string(word).c_str()); - - auto len = disp_len(word); - if(remaining >= len) - { - ss << word << (word.back() != '-' ? " " : ""); - - // printf("norm (%zu)", remaining); - remaining -= len; - - if(remaining > 0) - { - remaining -= 1; - } - else - { - ss << "\n" << LEFT_MARGIN; - remaining = tw; - } - - // printf("(%zu)\n", remaining); - } - else if(remaining < 3 || len < 5) - { - // for anything less than 5 chars, put it on the next line -- don't hyphenate. - ss << "\n" << LEFT_MARGIN << word << (word.back() != '-' ? " " : ""); - - // printf("next (%zu)", remaining); - remaining = tw - (len + 1); - // printf("(%zu)\n", remaining); - } - else - { - // split it. - ss << word.substr(0, remaining - 2) << "-" << "\n"; - ss << LEFT_MARGIN << word.substr(remaining - 2) << " "; - - // printf("split (%zu)", remaining); - remaining = tw - word.substr(remaining - 2).size(); - // printf("(%zu)\n", remaining); - } - } - - zpr::println(ss.str()); - } + auto xs = ztmu::prettyFormatTextBlock(helpLines, " ", " "); + for(const auto& x : xs) + zpr::println(x); } } + + + + + + diff --git a/source/repl/history.cpp b/source/repl/history.cpp index 5bd6a4c1..f8329a0e 100644 --- a/source/repl/history.cpp +++ b/source/repl/history.cpp @@ -17,7 +17,7 @@ namespace repl auto home = platform::getEnvironmentVar("HOME"); // do some checks so we don't try to write stuff into the root directory. - return (home + (home.empty() || home.back() == '/' ? "" : "/")) + ".flax-repl-history"; + return (home + ((home.empty() || home.back() == '/') ? "" : "/")) + ".flax-repl-history"; } diff --git a/source/typecheck/alloc.cpp b/source/typecheck/alloc.cpp index b98721a8..8a26d621 100644 --- a/source/typecheck/alloc.cpp +++ b/source/typecheck/alloc.cpp @@ -23,7 +23,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(this->attrs.has(attr::RAW) && this->counts.size() > 1) error(this, "only one length dimension is supported for raw memory allocation (have %d)", this->counts.size()); - std::vector counts = util::map(this->counts, [fs](ast::Expr* e) -> auto { + std::vector counts = zfu::map(this->counts, [fs](ast::Expr* e) -> auto { auto c = e->typecheck(fs, fir::Type::getNativeWord()).expr(); if(!c->type->isIntegerType()) error(c, "expected integer type ('i64') for alloc count, found '%s' instead", c->type); diff --git a/source/typecheck/arithmetic.cpp b/source/typecheck/arithmetic.cpp index 1c3909fd..bd3b91a3 100644 --- a/source/typecheck/arithmetic.cpp +++ b/source/typecheck/arithmetic.cpp @@ -24,7 +24,7 @@ static sst::FunctionDefn* getOverloadedOperator(sst::TypecheckState* fs, const L for(auto ovp : (*thelist)[op]) { - int dist = fs->getOverloadDistance(util::map(ovp->params, [](const auto& p) { return p.type; }), args); + int dist = fs->getOverloadDistance(zfu::map(ovp->params, [](const auto& p) { return p.type; }), args); if(dist == -1) continue; if(dist == best) @@ -194,7 +194,7 @@ fir::Type* sst::TypecheckState::getBinaryOpResultType(fir::Type* left, fir::Type return left; } } - else if(util::match(op, Operator::BitwiseOr, Operator::BitwiseAnd, Operator::BitwiseXor)) + else if(zfu::match(op, Operator::BitwiseOr, Operator::BitwiseAnd, Operator::BitwiseXor)) { if(left == right) return left; diff --git a/source/typecheck/call.cpp b/source/typecheck/call.cpp index 996b0c61..bb9c39bb 100644 --- a/source/typecheck/call.cpp +++ b/source/typecheck/call.cpp @@ -22,7 +22,7 @@ sst::Expr* ast::FunctionCall::typecheckWithArguments(sst::TypecheckState* fs, co { auto ret = util::pool(this->loc, ty); ret->callee = sst::TypeExpr::make(this->loc, ty); - ret->arguments = util::map(_arguments, [](const auto& e) -> sst::Expr* { return e.value; }); + ret->arguments = zfu::map(_arguments, [](const auto& e) -> sst::Expr* { return e.value; }); return ret; } @@ -122,9 +122,9 @@ sst::Expr* ast::ExprCall::typecheckWithArguments(sst::TypecheckState* fs, const error(this->callee, "expression with non-function-type '%s' cannot be called", target->type); auto ft = target->type->toFunctionType(); - auto [ dist, errs ] = sst::resolver::computeOverloadDistance(this->loc, util::map(ft->getArgumentTypes(), [](fir::Type* t) -> auto { + auto [ dist, errs ] = sst::resolver::computeOverloadDistance(this->loc, zfu::map(ft->getArgumentTypes(), [](fir::Type* t) -> auto { return fir::LocatedType(t, Location()); - }), util::map(arguments, [](const FnCallArgument& fca) -> fir::LocatedType { + }), zfu::map(arguments, [](const FnCallArgument& fca) -> fir::LocatedType { return fir::LocatedType(fca.value->type, fca.loc); }), target->type->toFunctionType()->isCStyleVarArg(), this->loc); @@ -139,7 +139,7 @@ sst::Expr* ast::ExprCall::typecheckWithArguments(sst::TypecheckState* fs, const auto ret = util::pool(this->loc, target->type->toFunctionType()->getReturnType()); ret->callee = target; - ret->arguments = util::map(arguments, [](const auto& e) -> sst::Expr* { return e.value; }); + ret->arguments = zfu::map(arguments, [](const auto& e) -> sst::Expr* { return e.value; }); return ret; } diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index e3904eae..49529f0d 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -53,7 +53,7 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* // why do we do this when generating the declaration instead of only when we typecheck? // as it currently stands, this means that our base class + any traits must appear before // this class definition in the source code, which is kinda dumb. - for(auto base : util::map(this->bases, [fs](auto t) -> auto { return fs->convertParserTypeToFIR(t); })) + for(auto base : zfu::map(this->bases, [fs](auto t) -> auto { return fs->convertParserTypeToFIR(t); })) { if(base->isClassType()) { diff --git a/source/typecheck/controlflow.cpp b/source/typecheck/controlflow.cpp index 23fcd226..e9a78512 100644 --- a/source/typecheck/controlflow.cpp +++ b/source/typecheck/controlflow.cpp @@ -26,7 +26,7 @@ TCResult ast::IfStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) //* here, it is implicit that all the inits of every case live in the same scope. //? we might want to change this eventually? i'm not sure. - auto inits = util::map(c.inits, [fs](Stmt* s) -> auto { return s->typecheck(fs).stmt(); }); + auto inits = zfu::map(c.inits, [fs](Stmt* s) -> auto { return s->typecheck(fs).stmt(); }); auto cs = Case(c.cond->typecheck(fs).expr(), dcast(sst::Block, c.body->typecheck(fs).stmt()), inits); if(!cs.cond->type->isBoolType() && !cs.cond->type->isPointerType()) diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 1bfe8931..3309fd87 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -106,7 +106,7 @@ static std::vector searchTransparentFields(sst::TypecheckState* flds = str->fields; else if(auto unn = dcast(sst::RawUnionDefn, defn); unn) - flds = util::map(util::pairs(unn->fields), [](const auto& x) -> auto { return x.second; }) + unn->transparentFields; + flds = zfu::map(unn->fields, zfu::pair_second()) + unn->transparentFields; else error(loc, "what kind of type is this? '%s'", ty); @@ -244,7 +244,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { // TODO: Extension support here fir::Type* res = 0; - if(util::match(vr->name, names::saa::FIELD_LENGTH, names::saa::FIELD_CAPACITY, + if(zfu::match(vr->name, names::saa::FIELD_LENGTH, names::saa::FIELD_CAPACITY, names::saa::FIELD_REFCOUNT, names::string::FIELD_COUNT)) { res = fir::Type::getNativeWord(); @@ -312,7 +312,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { fir::Type* res = 0; if(vr->name == names::saa::FIELD_LENGTH || (type->isDynamicArrayType() - && util::match(vr->name, names::saa::FIELD_CAPACITY, names::saa::FIELD_REFCOUNT))) + && zfu::match(vr->name, names::saa::FIELD_CAPACITY, names::saa::FIELD_REFCOUNT))) { res = fir::Type::getNativeWord(); } @@ -491,7 +491,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d // check methods first - std::vector arguments = util::map(fc->args, [fs](auto arg) -> FnCallArgument { + std::vector arguments = zfu::map(fc->args, [fs](auto arg) -> FnCallArgument { return FnCallArgument(arg.second->loc, arg.first, arg.second->typecheck(fs).expr(), arg.second); }); @@ -559,7 +559,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d else { auto c = util::pool(fc->loc, resolved->type->toFunctionType()->getReturnType()); - c->arguments = util::map(arguments, [](const FnCallArgument& e) -> sst::Expr* { return e.value; }); + c->arguments = zfu::map(arguments, [](const FnCallArgument& e) -> sst::Expr* { return e.value; }); auto tmp = util::pool(fc->loc, resolved->type); tmp->lhs = lhs; @@ -677,7 +677,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d { if(auto fld = dcast(ast::Ident, dotop->right)) { - auto flds = util::map(util::pairs(rnn->fields), [](const auto& x) -> auto { return x.second; }) + rnn->transparentFields; + auto flds = zfu::map(rnn->fields, zfu::pair_second()) + rnn->transparentFields; auto hmm = resolveFieldNameDotOp(fs, lhs, flds, dotop->loc, fld->name); if(hmm) { @@ -727,7 +727,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, sst::Expr* ret = 0; if(auto fc = dcast(ast::FunctionCall, dot->right)) { - auto args = util::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, + auto args = zfu::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, e.second->typecheck(fs).expr(), e.second); }); @@ -736,7 +736,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } else if(auto ec = dcast(ast::ExprCall, dot->right)) { - auto args = util::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, + auto args = zfu::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, e.second->typecheck(fs).expr(), e.second); }); @@ -760,7 +760,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, else { error(dot->right, "unexpected %s on right-side of dot-operator following static scope '%s' on the left", dot->right->readableName, - util::serialiseScope(news)); + zfu::join(news, "::")); } diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index d75a1b27..98a33e77 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -208,10 +208,10 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf defn->isIntrinsic = this->isIntrinsic; if(this->isVarArg) - defn->type = fir::FunctionType::getCVariadicFunc(util::map(ps, [](const FnParam& p) -> auto { return p.type; }), retty); + defn->type = fir::FunctionType::getCVariadicFunc(zfu::map(ps, [](const FnParam& p) -> auto { return p.type; }), retty); else - defn->type = fir::FunctionType::get(util::map(ps, [](const FnParam& p) -> auto { return p.type; }), retty); + defn->type = fir::FunctionType::get(zfu::map(ps, [](const FnParam& p) -> auto { return p.type; }), retty); auto conflict_err = fs->checkForShadowingOrConflictingDefinition(defn, [defn](sst::TypecheckState* fs, sst::Stmt* other) -> bool { @@ -223,8 +223,8 @@ TCResult ast::ForeignFuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* inf // check the typelists, then bool ret = fir::Type::areTypeListsEqual( - util::map(defn->params, [](const FnParam& p) -> fir::Type* { return p.type; }), - util::map(decl->params, [](const FnParam& p) -> fir::Type* { return p.type; }) + zfu::map(defn->params, [](const FnParam& p) -> fir::Type* { return p.type; }), + zfu::map(decl->params, [](const FnParam& p) -> fir::Type* { return p.type; }) ); return ret; diff --git a/source/typecheck/polymorph/driver.cpp b/source/typecheck/polymorph/driver.cpp index e9b0ab09..94ea30f6 100644 --- a/source/typecheck/polymorph/driver.cpp +++ b/source/typecheck/polymorph/driver.cpp @@ -203,7 +203,7 @@ namespace poly target.push_back(ArgType("", vty, uvloc)); } - auto given = util::map(input, [](const FnCallArgument& fca) -> ArgType { + auto given = zfu::map(input, [](const FnCallArgument& fca) -> ArgType { return ArgType(fca.name, fca.value->type, fca.loc); }); @@ -262,7 +262,7 @@ namespace poly { auto ift = problem_infer->toFunctionType(); - util::foreachIdx(ift->getArgumentTypes(), [&target, ¶ms](fir::Type* ty, size_t i) { + zfu::foreachIdx(ift->getArgumentTypes(), [&target, ¶ms](fir::Type* ty, size_t i) { target.push_back(ArgType(params[i].name, ty, params[i].loc)); }); @@ -273,7 +273,7 @@ namespace poly if(isFnCall) { - given = util::map(input, [](const FnCallArgument& a) -> poly::ArgType { + given = zfu::map(input, [](const FnCallArgument& a) -> poly::ArgType { return ArgType(a.name, a.value->type, a.loc, /* opt: */ false, /* ignoreName: */ a.ignoreName); }); @@ -292,7 +292,7 @@ namespace poly // ok, we should have it. iceAssert(type_infer->isFunctionType()); - given = util::mapidx(type_infer->toFunctionType()->getArgumentTypes(), [¶ms](fir::Type* t, size_t i) -> ArgType { + given = zfu::mapIdx(type_infer->toFunctionType()->getArgumentTypes(), [¶ms](fir::Type* t, size_t i) -> ArgType { return ArgType(params[i].name, t, params[i].loc); }) + ArgType("", type_infer->toFunctionType()->getReturnType(), thing->loc); } diff --git a/source/typecheck/polymorph/instantiator.cpp b/source/typecheck/polymorph/instantiator.cpp index b49132dd..d0d6376a 100644 --- a/source/typecheck/polymorph/instantiator.cpp +++ b/source/typecheck/polymorph/instantiator.cpp @@ -18,12 +18,12 @@ namespace poly static SimpleError* complainAboutMissingSolutions(const Location& l, ast::Parameterisable* thing, const std::vector& missing) { - auto strs = util::map(missing, [](fir::PolyPlaceholderType* p) -> std::string { + auto strs = zfu::map(missing, [](fir::PolyPlaceholderType* p) -> std::string { return p->str().substr(1); }); auto mstr = util::listToEnglish(strs); - return SimpleError::make(l, "type %s %s could not be inferred", util::plural("parameter", strs.size()), mstr); + return SimpleError::make(l, "type %s %s could not be inferred", zfu::plural("parameter", strs.size()), mstr); } std::vector getMissingSolutions(const ProblemSpace_t& needed, const TypeParamMap_t& solution, @@ -92,7 +92,7 @@ namespace poly return std::make_pair( TCResult(err->append(se)->append(SimpleError::make(fs->loc(), "partial solution: %s", - util::listToString(util::map(util::pairs(soln.solutions), [](const std::pair& p) -> std::string { + zfu::listToString(zfu::map(soln.solutions, [](const std::pair& p) -> std::string { return strprintf("%s = %s", p.first, p.second); }), [](const std::string& s) -> std::string { return s; @@ -204,7 +204,8 @@ namespace poly if(auto missing = internal::getMissingSolutions(thing->generics, mappings, true); missing.size() > 0) { auto mstr = util::listToEnglish(missing); - return TCResult(SimpleError::make(fs->loc(), "type %s %s could not be inferred", util::plural("parameter", missing.size()), mstr)); + return TCResult(SimpleError::make(fs->loc(), "type %s %s could not be inferred", + zfu::plural("parameter", missing.size()), mstr)); } return thing->typecheck(fs, self_infer, mappings); diff --git a/source/typecheck/polymorph/misc.cpp b/source/typecheck/polymorph/misc.cpp index 6ec447db..261e7e43 100644 --- a/source/typecheck/polymorph/misc.cpp +++ b/source/typecheck/polymorph/misc.cpp @@ -86,7 +86,7 @@ namespace sst int polysession) { // mm, smells functional. - return util::map(input, [fs, problems, polysession](pts::Type* pt) -> fir::Type* { + return zfu::map(input, [fs, problems, polysession](pts::Type* pt) -> fir::Type* { return convertPtsType(fs, problems, pt, polysession); }); } @@ -96,7 +96,7 @@ namespace sst std::vector unwrapFunctionParameters(TypecheckState* fs, const ProblemSpace_t& problems, const std::vector& args, int polysession) { - return util::mapidx(convertPtsTypeList(fs, problems, util::map(args, + return zfu::mapIdx(convertPtsTypeList(fs, problems, zfu::map(args, [](const ast::FuncDefn::Param& a) -> pts::Type* { return a.type; } diff --git a/source/typecheck/polymorph/solver.cpp b/source/typecheck/polymorph/solver.cpp index d15a867b..81db0257 100644 --- a/source/typecheck/polymorph/solver.cpp +++ b/source/typecheck/polymorph/solver.cpp @@ -110,22 +110,22 @@ namespace sst std::vector input; if(gt->isFunctionType()) { - input = util::map(gt->toFunctionType()->getArgumentTypes(), [given](fir::Type* t) -> ArgType { + input = zfu::map(gt->toFunctionType()->getArgumentTypes(), [given](fir::Type* t) -> ArgType { return ArgType("", t, given.loc); }) + ArgType("", gt->toFunctionType()->getReturnType(), given.loc); - problem = util::map(tt->toFunctionType()->getArgumentTypes(), [target](fir::Type* t) -> ArgType { + problem = zfu::map(tt->toFunctionType()->getArgumentTypes(), [target](fir::Type* t) -> ArgType { return ArgType("", t, target.loc); }) + ArgType("", tt->toFunctionType()->getReturnType(), target.loc); } else { iceAssert(gt->isTupleType()); - input = util::map(gt->toTupleType()->getElements(), [given](fir::Type* t) -> ArgType { + input = zfu::map(gt->toTupleType()->getElements(), [given](fir::Type* t) -> ArgType { return ArgType("", t, given.loc); }); - problem = util::map(tt->toTupleType()->getElements(), [target](fir::Type* t) -> ArgType { + problem = zfu::map(tt->toTupleType()->getElements(), [target](fir::Type* t) -> ArgType { return ArgType("", t, target.loc); }); } @@ -155,13 +155,13 @@ namespace sst // either we have all names or no names for the target! if(target.size() > 0 && target[0].name != "") { - util::foreachIdx(target, [&targetnames](const ArgType& t, size_t i) { + zfu::foreachIdx(target, [&targetnames](const ArgType& t, size_t i) { targetnames[t.name] = i; }); } std::set unsolvedtargets; - util::foreachIdx(target, [&unsolvedtargets, &target, fvararg](const ArgType& a, size_t i) { + zfu::foreachIdx(target, [&unsolvedtargets, &target, fvararg](const ArgType& a, size_t i) { // if it's optional, we don't mark it as 'unsolved'. if((!fvararg || i + 1 != target.size()) && !a.optional) unsolvedtargets.insert(i); @@ -308,7 +308,7 @@ namespace sst if(targetnames.empty() || given.size() > target.size()) { return SimpleError::make(callLoc, "expected %d %s, but %d %s provided", - target.size(), util::plural("argument", target.size()), given.size(), given.size() == 1 ? "was" : "were"); + target.size(), zfu::plural("argument", target.size()), given.size(), given.size() == 1 ? "was" : "were"); } else { @@ -317,8 +317,8 @@ namespace sst missings.push_back(target[us].name); auto s = util::listToEnglish(missings, /* quote: */ true); - return SimpleError::make(callLoc, "missing %s for %s %s", util::plural("argument", missings.size()), - util::plural("parameter", missings.size()), s); + return SimpleError::make(callLoc, "missing %s for %s %s", zfu::plural("argument", missings.size()), + zfu::plural("parameter", missings.size()), s); } } diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 8b22696c..4d07478a 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -20,7 +20,7 @@ namespace resolver TCResult resolveFunctionCallFromCandidates(TypecheckState* fs, const Location& callLoc, const std::vector& cands, std::vector* args, const PolyArgMapping_t& gmaps, bool allowImplicitSelf) { - auto cds = util::map(cands, [&args](auto c) -> std::pair> { return { c, *args }; }); + auto cds = zfu::map(cands, [&args](auto c) -> std::pair> { return { c, *args }; }); auto [ ret, new_args ] = resolver::internal::resolveFunctionCallFromCandidates(fs, fs->loc(), cds, gmaps, allowImplicitSelf, nullptr); *args = new_args; @@ -114,6 +114,13 @@ namespace resolver { if(!didGeneric) { + auto top = fs->stree; + while(top && top->parent) + top = top->parent; + + // just dump this. + top->dump(); + return TCResult(SimpleError::make(fs->loc(), "no function named '%s' in the current scope", name)); } else @@ -182,7 +189,7 @@ namespace resolver auto copy1 = copy; - auto cand = resolveFunctionCallFromCandidates(fs, callLoc, util::map(cls->initialisers, [](auto e) -> auto { + auto cand = resolveFunctionCallFromCandidates(fs, callLoc, zfu::map(cls->initialisers, [](auto e) -> auto { return dcast(sst::Defn, e); }), ©, pams, true); @@ -217,17 +224,17 @@ namespace resolver else { auto seencopy = seen; - std::vector target = util::filterMap(str->fields, [&seencopy](sst::StructFieldDefn* f) -> bool { + std::vector target = zfu::filterMap(str->fields, [&seencopy](sst::StructFieldDefn* f) -> bool { return seencopy.find(f->id.name) != seencopy.end(); }, [](sst::StructFieldDefn* f) -> FnParam { return FnParam(f->loc, f->id.name, f->type); }); - auto args = util::map(arguments, [](const FnCallArgument& a) -> poly::ArgType { + auto args = zfu::map(arguments, [](const FnCallArgument& a) -> poly::ArgType { return poly::ArgType(a.name, a.value->type, a.loc); }); - auto [ soln, err ] = poly::solveTypeList(fs->loc(), util::map(target, [](const FnParam& f) -> poly::ArgType { + auto [ soln, err ] = poly::solveTypeList(fs->loc(), zfu::map(target, [](const FnParam& f) -> poly::ArgType { return poly::ArgType(f.name, f.type, f.loc, f.defaultVal != 0); }), args, poly::Solution_t(), /* isFnCall: */ true); diff --git a/source/typecheck/resolver/misc.cpp b/source/typecheck/resolver/misc.cpp index dabb52bc..2390e843 100644 --- a/source/typecheck/resolver/misc.cpp +++ b/source/typecheck/resolver/misc.cpp @@ -62,7 +62,7 @@ namespace sst std::vector typecheckCallArguments(TypecheckState* fs, const std::vector>& args) { - return util::map(args, [fs](const auto& a) -> FnCallArgument { + return zfu::map(args, [fs](const auto& a) -> FnCallArgument { return FnCallArgument(a.second->loc, a.first, a.second->typecheck(fs).expr(), a.second); }); } @@ -131,7 +131,7 @@ namespace sst target.push_back(fir::LocatedType(vty, uvd->loc)); } - auto [ dist, errs ] = resolver::computeOverloadDistance(unn->loc, target, util::map(*arguments, [](const FnCallArgument& fca) -> auto { + auto [ dist, errs ] = resolver::computeOverloadDistance(unn->loc, target, zfu::map(*arguments, [](const FnCallArgument& fca) -> auto { return fir::LocatedType(fca.value->type, fca.loc); }), /* isCVarArg: */ false, fs->loc()); @@ -220,17 +220,17 @@ namespace sst int TypecheckState::getOverloadDistance(const std::vector& a, const std::vector& b) { - return resolver::computeOverloadDistance(Location(), util::map(a, [](fir::Type* t) -> fir::LocatedType { + return resolver::computeOverloadDistance(Location(), zfu::map(a, [](fir::Type* t) -> fir::LocatedType { return fir::LocatedType(t, Location()); - }), util::map(b, [](fir::Type* t) -> fir::LocatedType { + }), zfu::map(b, [](fir::Type* t) -> fir::LocatedType { return fir::LocatedType(t, Location()); }), /* isCVarArg: */ false, this->loc()).first; } bool TypecheckState::isDuplicateOverload(const std::vector& a, const std::vector& b) { - return this->getOverloadDistance(util::map(a, [](const auto& p) -> auto { return p.type; }), - util::map(b, [](const auto& p) -> auto { return p.type; })) == 0; + return this->getOverloadDistance(zfu::map(a, [](const auto& p) -> auto { return p.type; }), + zfu::map(b, [](const auto& p) -> auto { return p.type; })) == 0; } } diff --git a/source/typecheck/resolver/resolver.cpp b/source/typecheck/resolver/resolver.cpp index eae4a25c..5a235002 100644 --- a/source/typecheck/resolver/resolver.cpp +++ b/source/typecheck/resolver/resolver.cpp @@ -17,14 +17,14 @@ namespace resolver const std::vector& _args, bool cvararg, const Location& callLoc) { std::vector _input; - if(cvararg) _input = util::take(_args, _target.size()); + if(cvararg) _input = zfu::take(_args, _target.size()); else _input = _args; - auto input = util::map(_input, [](auto t) -> poly::ArgType { + auto input = zfu::map(_input, [](auto t) -> poly::ArgType { return poly::ArgType("", t.type, t.loc); }); - auto target = util::map(_target, [](auto t) -> poly::ArgType { + auto target = zfu::map(_target, [](auto t) -> poly::ArgType { return poly::ArgType("", t.type, t.loc); }); @@ -40,10 +40,10 @@ namespace resolver const std::vector& _args, bool cvararg, const Location& callLoc) { std::vector input; - if(cvararg) input = util::take(_args, target.size()); + if(cvararg) input = zfu::take(_args, target.size()); else input = _args; - auto arguments = util::map(input, [](const FnCallArgument& a) -> poly::ArgType { + auto arguments = zfu::map(input, [](const FnCallArgument& a) -> poly::ArgType { return poly::ArgType(a.name, a.value->type, a.loc, /* opt: */ false, /* ignoreName: */ a.ignoreName); }); @@ -51,7 +51,7 @@ namespace resolver // the type-list solver. it doesn't need to know what the actual value is --- when we typechecked the function, we should // have already verified the default value fits the type, and it doesn't actually change the type of the receiver. - auto [ soln, err1 ] = poly::solveTypeList(callLoc, util::map(target, [](const FnParam& p) -> poly::ArgType { + auto [ soln, err1 ] = poly::solveTypeList(callLoc, zfu::map(target, [](const FnParam& p) -> poly::ArgType { return poly::ArgType(p.name, p.type, p.loc, p.defaultVal != 0); }), arguments, poly::Solution_t(), /* isFnCall: */ true); @@ -87,10 +87,10 @@ namespace resolver } else { - std::vector tmp = util::map(args, [](const FnCallArgument& p) -> auto { return p.value->type; }); + std::vector tmp = zfu::map(args, [](const FnCallArgument& p) -> auto { return p.value->type; }); auto errs = OverloadError::make(SimpleError::make(callLoc, "no overload in call to '%s' with arguments (%s) amongst %d %s", - name, fir::Type::typeListToString(tmp), fails.size(), util::plural("candidate", fails.size()))); + name, fir::Type::typeListToString(tmp), fails.size(), zfu::plural("candidate", fails.size()))); for(auto f : fails) { @@ -244,9 +244,9 @@ namespace resolver } auto prms = ft->getArgumentTypes(); - std::tie(dist, fails[vr]) = computeOverloadDistance(curcandidate->loc, util::map(prms, [](fir::Type* t) -> fir::LocatedType { + std::tie(dist, fails[vr]) = computeOverloadDistance(curcandidate->loc, zfu::map(prms, [](fir::Type* t) -> fir::LocatedType { return fir::LocatedType(t, Location()); - }), util::map(replacementArgs, [](const FnCallArgument& p) -> fir::LocatedType { + }), zfu::map(replacementArgs, [](const FnCallArgument& p) -> fir::LocatedType { return fir::LocatedType(p.value->type, Location()); }), /* isCVarArg: */ false, callLoc); } @@ -302,7 +302,7 @@ namespace resolver if(finals.empty()) { auto err = createErrorFromFailedCandidates(fs, callLoc, cands[0].first->id.name, cands[0].second, - util::map(util::pairs(fails), [](auto p) -> std::pair { + zfu::map(zfu::pairs(fails), [](auto p) -> std::pair { return std::make_pair(p.first, p.second); })); diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index 1316f07a..bc499cce 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -68,7 +68,7 @@ static void _checkTransparentFieldRedefinition(sst::TypecheckState* fs, sst::Typ flds = str->fields; else if(auto unn = dcast(sst::RawUnionDefn, innerdef); unn) - flds = util::map(util::pairs(unn->fields), [](const auto& x) -> auto { return x.second; }) + unn->transparentFields; + flds = zfu::map(unn->fields, zfu::pair_second()) + unn->transparentFields; else error(fs->loc(), "what kind of type is this? '%s'", ty); diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 93bb9476..78292a03 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -38,19 +38,27 @@ namespace sst return clone; } - static StateTree* _addTreeToExistingTree(const std::unordered_set& thingsImported, StateTree* existing, StateTree* _tree, - StateTree* commonParent, bool pubImport, bool ignoreVis, const std::string& importer) + static StateTree* _addTreeToExistingTree(const std::unordered_set& thingsImported, StateTree* existing, + StateTree* tree, StateTree* commonParent, bool pubImport, bool ignoreVis, const std::string& importer) { - // StateTree* tree = cloneTree(_tree, commonParent); - StateTree* tree = _tree; + // if it was compiler-generated, just skip it. + if(tree->isCompilerGenerated) + return existing; // first merge all children -- copy whatever 1 has, plus what 1 and 2 have in common for(auto sub : tree->subtrees) { + if(sub.second->isCompilerGenerated) + continue; + // debuglog("add subtree '%s' (%p) to tree '%s' (%p)\n", sub.first, sub.second, existing->name, existing); if(auto it = existing->subtrees.find(sub.first); it != existing->subtrees.end()) { - _addTreeToExistingTree(thingsImported, existing->subtrees[sub.first], sub.second, existing, pubImport, ignoreVis, importer); + if(sub.second->treeDefn->visibility != VisibilityLevel::Public) + continue; + + _addTreeToExistingTree(thingsImported, existing->subtrees[sub.first], sub.second, existing, + pubImport, ignoreVis, importer); } else { @@ -89,9 +97,11 @@ namespace sst } else if(auto f = dcast(FunctionDecl, ot)) { - if(fir::Type::areTypeListsEqual(util::map(fn->params, [](const auto& p) -> fir::Type* { return p.type; }), - util::map(f->params, [](const auto& p) -> fir::Type* { return p.type; }))) + if(fir::Type::areTypeListsEqual(zfu::map(fn->params, [](const auto& p) -> fir::Type* { return p.type; }), + zfu::map(f->params, [](const auto& p) -> fir::Type* { return p.type; }))) { + debuglogln("from: %s", tree->topLevelFilename); + debuglogln("to: %s", existing->topLevelFilename); SimpleError::make(fn->loc, "duplicate definition of function '%s' with identical signature", fn->id.name) ->append(SimpleError::make(MsgType::Note, f->loc, "conflicting definition was here: (%p vs %p)", reinterpret_cast(f), reinterpret_cast(fn))) @@ -105,7 +115,7 @@ namespace sst } else if(auto vr = dcast(VarDefn, def)) { - auto err = SimpleError::make(vr->loc, "duplicate definition for variable '%s'"); + auto err = SimpleError::make(vr->loc, "duplicate definition for variable '%s'", name); for(auto ot : others) err->append(SimpleError::make(MsgType::Note, ot->loc, "previously defined here:")); @@ -129,18 +139,19 @@ namespace sst else { // probably a class or something - conflict: + conflict: SimpleError::make(def->loc, "duplicate definition of %s '%s'", def->readableName, def->id.name) ->append(SimpleError::make(MsgType::Note, ot->loc, "conflicting definition was here:")) ->postAndQuit(); } } + warn(def, "add to %s", existing->topLevelFilename); existing->addDefinition(tree->topLevelFilename, name, def); } else { - // warn(def, "skipping def %s because it is not public", def->id.name); + warn(def, "skipping def %s because it is not public", def->id.name); } } } @@ -182,9 +193,9 @@ namespace sst return existing; } - StateTree* addTreeToExistingTree(StateTree* existing, StateTree* _tree, StateTree* commonParent, bool pubImport, bool ignoreVis) + StateTree* addTreeToExistingTree(StateTree* existing, StateTree* tree, StateTree* commonParent, bool pubImport, bool ignoreVis) { - return _addTreeToExistingTree({ }, existing, _tree, commonParent, pubImport, ignoreVis, existing->topLevelFilename); + return _addTreeToExistingTree({ }, existing, tree, commonParent, pubImport, ignoreVis, existing->topLevelFilename); } @@ -250,9 +261,13 @@ namespace sst static void generatePreludeDefinitions(TypecheckState* fs) { auto loc = Location(); + loc.fileID = frontend::getFileIDFromFilename(fs->stree->topLevelFilename); + auto strings = getOsStrings(); fs->pushTree("os"); + fs->stree->isCompilerGenerated = true; + defer(fs->popTree()); // manually add the definition, because we didn't typecheck a namespace or anything. @@ -267,6 +282,7 @@ namespace sst name_def->type = strty; name_def->global = true; name_def->immutable = true; + name_def->visibility = VisibilityLevel::Private; auto s = util::pool(loc, strty); s->str = strings.name; @@ -281,6 +297,7 @@ namespace sst vendor_def->type = strty; vendor_def->global = true; vendor_def->immutable = true; + vendor_def->visibility = VisibilityLevel::Private; auto s = util::pool(loc, strty); s->str = strings.vendor; @@ -309,6 +326,8 @@ namespace sst for(auto [ ithing, import ] : imports) { + info(ithing.loc, "import: %s", ithing.name); + auto ias = ithing.importAs; if(ias.empty()) ias = cs->parsed[ithing.name].modulePath + cs->parsed[ithing.name].moduleName; @@ -346,7 +365,9 @@ namespace sst treedef->id = Identifier(impas, IdKind::Name); treedef->tree = newinspt; treedef->tree->treeDefn = treedef; - treedef->visibility = VisibilityLevel::Public; + treedef->visibility = ithing.pubImport + ? VisibilityLevel::Public + : VisibilityLevel::Private; curinspt->addDefinition(file.name, impas, treedef); diff --git a/source/typecheck/type.cpp b/source/typecheck/type.cpp index 817601f4..30bf2ac4 100644 --- a/source/typecheck/type.cpp +++ b/source/typecheck/type.cpp @@ -218,7 +218,8 @@ namespace sst extraHelp = SimpleError::make(MsgType::Note, d->loc, "'%s' was defined as a type in the parent scope, here:", name) - ->append(BareError::make(MsgType::Note, "to refer to it, use '%s'", util::serialiseScope(ss))); + ->append(BareError::make(MsgType::Note, "to refer to it, use '%s'", + zfu::join(ss, "::"))); } } } diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index c59cf375..2d709393 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -212,7 +212,7 @@ namespace sst tree = tree->parent; } - return util::serialiseScope(std::vector(scope.begin(), scope.end())); + return zfu::join(std::vector(scope.begin(), scope.end()), "::"); } std::vector TypecheckState::getCurrentScope() @@ -263,7 +263,7 @@ namespace sst // if(auto it = tree->subtrees.find(s); it == tree->subtrees.end()) // { - // error(this->loc(), "no such tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, util::serialiseScope(scope)); + // error(this->loc(), "no such tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, zfu::join(scope, "::")); // } // else // { @@ -284,7 +284,8 @@ namespace sst if(auto it = tree->subtrees.find(s); it == tree->subtrees.end()) { - error(this->loc(), "nonexistent tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, util::serialiseScope(scope)); + error(this->loc(), "nonexistent tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, + zfu::join(scope, "::")); } else { @@ -471,7 +472,7 @@ namespace sst for(const auto& [ l, kind ] : conflicts) { err->append(SimpleError::make(MsgType::Note, l->loc, "%shere%s:", first ? strprintf("conflicting %s ", - util::plural("definition", conflicts.size())) : "and ", ak == kind ? "" : strprintf(" (as a %s)", kind))); + zfu::plural("definition", conflicts.size())) : "and ", ak == kind ? "" : strprintf(" (as a %s)", kind))); first = false; } @@ -494,8 +495,8 @@ namespace sst { auto a = dcast(sst::FunctionDecl, defn); auto b = dcast(sst::FunctionDecl, otherdef); - if(fir::Type::areTypeListsEqual(util::map(a->params, [](const auto& p) -> fir::Type* { return p.type; }), - util::map(b->params, [](const auto& p) -> fir::Type* { return p.type; }))) + if(fir::Type::areTypeListsEqual(zfu::map(a->params, [](const auto& p) -> fir::Type* { return p.type; }), + zfu::map(b->params, [](const auto& p) -> fir::Type* { return p.type; }))) { errs->append(BareError::make(MsgType::Note, "functions cannot be overloaded over argument names or" " return types alone")); @@ -520,7 +521,7 @@ namespace sst // honestly we can't know if we will conflict with other functions. // filter out by kind. - auto newgds = util::filterMap(gdefs, + auto newgds = zfu::filterMap(gdefs, [](ast::Parameterisable* d) -> bool { return dcast(ast::FuncDefn, d) == nullptr; }, @@ -536,7 +537,7 @@ namespace sst { // assume everything conflicts, since functions are the only thing that can overload. return makeTheError(defn, defn->id.name, defn->getKind(), - util::map(gdefs, [](ast::Parameterisable* d) -> std::pair { + zfu::map(gdefs, [](ast::Parameterisable* d) -> std::pair { return std::make_pair(d, d->getKind()); }) ); @@ -554,6 +555,43 @@ namespace sst } + // UWU + static int indent = 0; + void StateTree::dump() + { + zpr::println("%*s* TREE: %s - %s", indent * 2, "", this->name, frontend::getFilenameFromPath(this->topLevelFilename)); + for(const auto& [ filename, dm ] : this->definitions) + { + zpr::println("%*s* FROM %s (%s)", (indent + 1) * 2, "", frontend::getFilenameFromPath(filename), + dm.wasPublicImport ? "public" : "private"); + + for(const auto& [ name, defs ] : dm.defns) + { + zpr::println("%*s* %s", (indent + 2) * 2, "", name); + for(auto d : defs) + zpr::println("%*s> %s: %s", (indent + 3) * 2, "", d->id.str(), d->type ? d->type->str() : "??"); + } + } + + if(!this->subtrees.empty()) + { + zpr::println("\n%*s* SUBTREES:", (indent + 1) * 2, ""); + indent += 2; + for(auto& [ name, sub ] : this->subtrees) + { + if('0' <= name[0] && name[0] <= '9') + continue; + + sub->dump(); + } + + zpr::println("\n"); + indent -= 2; + } + } + + + Scope::Scope(StateTree* st) { this->next = 0; diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index f7f68975..5a0f4448 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -16,8 +16,10 @@ static void importScopeContentsIntoNewScope(sst::TypecheckState* fs, const std:: if(auto defs = parent->getDefinitionsWithName(name); !defs.empty()) { - auto err = SimpleError::make(fs->loc(), "cannot use import scope '%s' into scope '%s' with name '%s'; one or more conflicting definitions exist", - util::serialiseScope(sfrom), util::serialiseScope(stoParent), name); + auto err = SimpleError::make(fs->loc(), + "cannot use import scope '%s' into scope '%s' with name '%s'; one or more conflicting definitions exist", + zfu::join(sfrom, "::"), zfu::join(stoParent, "::"), + name); for(const auto& d : defs) err->append(SimpleError::make(MsgType::Note, d->loc, "conflicting definition here:")); From c9a7d84612788957d965c1ffe5aa2873607c3105 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Thu, 26 Nov 2020 16:50:09 +0800 Subject: [PATCH 108/129] update to llvm 11 --- source/backend/llvm/jit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/backend/llvm/jit.cpp b/source/backend/llvm/jit.cpp index 35e04e5e..4d5616d7 100644 --- a/source/backend/llvm/jit.cpp +++ b/source/backend/llvm/jit.cpp @@ -47,7 +47,7 @@ namespace backend OptimiseLayer(ES, CompileLayer, optimiseModule), DL(std::move(DL)), Mangle(ES, this->DL), Ctx(std::make_unique()), - dylib(ES.createJITDylib("")) + dylib(ES.createJITDylib("").get()) { llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); dylib.addGenerator(llvm::cantFail( From 24de18f24f897f54cdc88b69b1d7dbd9450ebea6 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 27 Nov 2020 13:15:41 +0800 Subject: [PATCH 109/129] partially implement slightly better import mechanism --- source/fir/interp/interpreter.cpp | 2 +- source/include/typecheck.h | 8 +- source/typecheck/toplevel.cpp | 227 +++++++++++++++------------- source/typecheck/typecheckstate.cpp | 26 +++- 4 files changed, 147 insertions(+), 116 deletions(-) diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 2d07466f..319ba54c 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -534,7 +534,7 @@ namespace interp // propagate to the backend code generation. void InterpState::finalise() { - for(const auto [ id, glob ] : this->module->_getGlobals()) + for(const auto& [ id, glob ] : this->module->_getGlobals()) { // printf("global: %s\n", id.str().c_str()); diff --git a/source/include/typecheck.h b/source/include/typecheck.h index bc22bacf..421d0154 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -52,9 +52,7 @@ namespace sst Scope(StateTree* st); StateTree* stree = 0; - const Scope* prev = 0; - const Scope* next = 0; std::vector getStrings() const; }; @@ -95,6 +93,12 @@ namespace sst // so we can resolve the import duplication bullshit util::hash_map definitions; + + std::vector exports; + std::vector imports; + std::vector reexports; + + // what's there to explain? a simple map of operators to their functions. we use // function overload resolution to determine which one to call, and ambiguities are // handled the usual way. diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 78292a03..203dfc6a 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -97,13 +97,15 @@ namespace sst } else if(auto f = dcast(FunctionDecl, ot)) { - if(fir::Type::areTypeListsEqual(zfu::map(fn->params, [](const auto& p) -> fir::Type* { return p.type; }), + if(fir::Type::areTypeListsEqual(zfu::map(fn->params, [](const auto& p) -> auto { return p.type; }), zfu::map(f->params, [](const auto& p) -> fir::Type* { return p.type; }))) { debuglogln("from: %s", tree->topLevelFilename); debuglogln("to: %s", existing->topLevelFilename); - SimpleError::make(fn->loc, "duplicate definition of function '%s' with identical signature", fn->id.name) - ->append(SimpleError::make(MsgType::Note, f->loc, "conflicting definition was here: (%p vs %p)", + SimpleError::make(fn->loc, "duplicate definition of function '%s' with identical signature", + fn->id.name) + ->append(SimpleError::make(MsgType::Note, f->loc, + "conflicting definition was here: (%p vs %p)", reinterpret_cast(f), reinterpret_cast(fn))) ->postAndQuit(); } @@ -205,108 +207,8 @@ namespace sst std::string name; std::string vendor; }; - - static OsStrings getOsStrings() - { - // TODO: handle cygwin/msys/mingw??? - // like how do we want to expose these? at the end of the day the os is still windows... - - OsStrings ret; - - #if defined(_WIN32) - ret.name = "windows"; - ret.vendor = "microsoft"; - #elif __MINGW__ - ret.name = "mingw"; - #elif __CYGWIN__ - ret.name = "cygwin"; - #elif __APPLE__ - ret.vendor = "apple"; - #include "TargetConditionals.h" - #if TARGET_IPHONE_SIMULATOR - ret.name = "iossimulator"; - #elif TARGET_OS_IOS - ret.name = "ios"; - #elif TARGET_OS_WATCH - ret.name = "watchos"; - #elif TARGET_OS_TV - ret.name = "tvos"; - #elif TARGET_OS_OSX - ret.name = "macos"; - #else - #error "unknown apple operating system" - #endif - #elif __ANDROID__ - ret.vendor = "google"; - ret.name = "android"; - #elif __linux__ || __linux || linux - ret.name = "linux"; - #elif __FreeBSD__ - ret.name = "freebsd"; - #elif __OpenBSD__ - ret.name = "openbsd"; - #elif __NetBSD__ - ret.name = "netbsd"; - #elif __DragonFly__ - ret.name = "dragonflybsd"; - #elif __unix__ - ret.name = "unix"; - #elif defined(_POSIX_VERSION) - ret.name = "posix"; - #endif - - return ret; - } - - static void generatePreludeDefinitions(TypecheckState* fs) - { - auto loc = Location(); - loc.fileID = frontend::getFileIDFromFilename(fs->stree->topLevelFilename); - - auto strings = getOsStrings(); - - fs->pushTree("os"); - fs->stree->isCompilerGenerated = true; - - defer(fs->popTree()); - - // manually add the definition, because we didn't typecheck a namespace or anything. - fs->stree->parent->addDefinition(fs->stree->name, fs->stree->treeDefn); - - auto strty = fir::Type::getCharSlice(false); - - { - // add the name - auto name_def = util::pool(loc); - name_def->id = Identifier("name", IdKind::Name); - name_def->type = strty; - name_def->global = true; - name_def->immutable = true; - name_def->visibility = VisibilityLevel::Private; - - auto s = util::pool(loc, strty); - s->str = strings.name; - - name_def->init = s; - fs->stree->addDefinition("name", name_def); - } - { - // add the name - auto vendor_def = util::pool(loc); - vendor_def->id = Identifier("vendor", IdKind::Name); - vendor_def->type = strty; - vendor_def->global = true; - vendor_def->immutable = true; - vendor_def->visibility = VisibilityLevel::Private; - - auto s = util::pool(loc, strty); - s->str = strings.vendor; - - vendor_def->init = s; - fs->stree->addDefinition("vendor", vendor_def); - } - } - + static OsStrings getOsStrings(); + static void generatePreludeDefinitions(TypecheckState* fs); @@ -380,8 +282,13 @@ namespace sst iceAssert(insertPoint); - _addTreeToExistingTree(fs->dtree->thingsImported, insertPoint, import->base, /* commonParent: */ nullptr, ithing.pubImport, - /* ignoreVis: */ false, file.name); + insertPoint->imports.push_back(import->base); + if(ithing.pubImport) + insertPoint->reexports.push_back(import->base); + + // _addTreeToExistingTree(fs->dtree->thingsImported, insertPoint, import->base, + // /* commonParent: */ nullptr, ithing.pubImport, + // /* ignoreVis: */ false, file.name); fs->dtree->thingsImported.insert(ithing.name); fs->dtree->typeDefnMap.insert(import->typeDefnMap.begin(), import->typeDefnMap.end()); @@ -404,13 +311,114 @@ namespace sst fs->dtree->topLevel = tns; } - catch (ErrorException& ee) + catch(ErrorException& ee) { ee.err->postAndQuit(); } return fs->dtree; } + + static OsStrings getOsStrings() + { + // TODO: handle cygwin/msys/mingw??? + // like how do we want to expose these? at the end of the day the os is still windows... + + OsStrings ret; + + #if defined(_WIN32) + ret.name = "windows"; + ret.vendor = "microsoft"; + #elif __MINGW__ + ret.name = "mingw"; + #elif __CYGWIN__ + ret.name = "cygwin"; + #elif __APPLE__ + ret.vendor = "apple"; + #include "TargetConditionals.h" + #if TARGET_IPHONE_SIMULATOR + ret.name = "iossimulator"; + #elif TARGET_OS_IOS + ret.name = "ios"; + #elif TARGET_OS_WATCH + ret.name = "watchos"; + #elif TARGET_OS_TV + ret.name = "tvos"; + #elif TARGET_OS_OSX + ret.name = "macos"; + #else + #error "unknown apple operating system" + #endif + #elif __ANDROID__ + ret.vendor = "google"; + ret.name = "android"; + #elif __linux__ || __linux || linux + ret.name = "linux"; + #elif __FreeBSD__ + ret.name = "freebsd"; + #elif __OpenBSD__ + ret.name = "openbsd"; + #elif __NetBSD__ + ret.name = "netbsd"; + #elif __DragonFly__ + ret.name = "dragonflybsd"; + #elif __unix__ + ret.name = "unix"; + #elif defined(_POSIX_VERSION) + ret.name = "posix"; + #endif + + return ret; + } + + static void generatePreludeDefinitions(TypecheckState* fs) + { + auto loc = Location(); + loc.fileID = frontend::getFileIDFromFilename(fs->stree->topLevelFilename); + + auto strings = getOsStrings(); + + fs->pushTree("os"); + fs->stree->isCompilerGenerated = true; + + defer(fs->popTree()); + + // manually add the definition, because we didn't typecheck a namespace or anything. + fs->stree->parent->addDefinition(fs->stree->name, fs->stree->treeDefn); + + auto strty = fir::Type::getCharSlice(false); + + { + // add the name + auto name_def = util::pool(loc); + name_def->id = Identifier("name", IdKind::Name); + name_def->type = strty; + name_def->global = true; + name_def->immutable = true; + name_def->visibility = VisibilityLevel::Private; + + auto s = util::pool(loc, strty); + s->str = strings.name; + + name_def->init = s; + fs->stree->addDefinition("name", name_def); + } + { + // add the name + auto vendor_def = util::pool(loc); + vendor_def->id = Identifier("vendor", IdKind::Name); + vendor_def->type = strty; + vendor_def->global = true; + vendor_def->immutable = true; + vendor_def->visibility = VisibilityLevel::Private; + + auto s = util::pool(loc, strty); + s->str = strings.vendor; + + vendor_def->init = s; + fs->stree->addDefinition("vendor", vendor_def); + } + } } @@ -468,6 +476,9 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer else if(!tcr.isParametric() && !tcr.isDummy()) ret->statements.push_back(tcr.stmt()); + if(tcr.isDefn() && tcr.defn()->visibility == VisibilityLevel::Public) + tree->exports.push_back(tcr.defn()); + // check for compiler support so we can add it to the big list of things. if((tcr.isStmt() || tcr.isDefn()) && tcr.stmt()->attrs.has(strs::attrs::COMPILER_SUPPORT)) { diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 2d709393..3d1dbaf0 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -311,19 +311,36 @@ namespace sst return ret; } - std::vector StateTree::getDefinitionsWithName(const std::string& name) + static void fetchDefinitionsFrom(const std::string& name, StateTree* tree, bool recursively, std::vector& out) { - std::vector ret; - for(const auto& [ filename, defnMap ] : this->definitions) + for(const auto& [ filename, defnMap ] : tree->definitions) { (void) filename; if(auto it = defnMap.defns.find(name); it != defnMap.defns.end()) { const auto& defs = it->second; - if(defs.size() > 0) ret.insert(ret.end(), defs.begin(), defs.end()); + if(defs.size() > 0) + out.insert(out.end(), defs.begin(), defs.end()); } } + if(!recursively) + return; + + for(auto import : tree->imports) + { + fetchDefinitionsFrom(name, import, false, out); + + for(auto reexp : import->reexports) + fetchDefinitionsFrom(name, reexp, false, out); + } + } + + std::vector StateTree::getDefinitionsWithName(const std::string& name) + { + std::vector ret; + fetchDefinitionsFrom(name, this, true, ret); + return ret; } @@ -594,7 +611,6 @@ namespace sst Scope::Scope(StateTree* st) { - this->next = 0; this->prev = 0; this->stree = st; From 5a8c30b528262793756f906627cf8c1786585e1e Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 28 Nov 2020 02:34:59 +0800 Subject: [PATCH 110/129] make enumdefs use the new scope teleporter --- .gitignore | 1 + build/tiniest.flx | 24 ++++ build/tmp/repro_1.flx | 8 +- build/tmp2/a.flx | 12 ++ build/tmp2/b.flx | 7 ++ build/tmp2/c.flx | 12 ++ build/ultratiny.flx | 10 +- notes.md | 19 ++++ repro_1 | Bin 12700 -> 0 bytes source/include/ast.h | 2 + source/include/sst.h | 1 + source/include/sst_expr.h | 1 + source/include/stcommon.h | 13 +++ source/include/typecheck.h | 19 ++-- source/repl/execute.cpp | 3 + source/typecheck/dotop.cpp | 72 +++++++++++- source/typecheck/enums.cpp | 13 +-- source/typecheck/structs.cpp | 163 ++++++++++++++-------------- source/typecheck/toplevel.cpp | 1 + source/typecheck/traits.cpp | 7 +- source/typecheck/typecheckstate.cpp | 73 ++++++++++--- 21 files changed, 328 insertions(+), 133 deletions(-) create mode 100644 build/tiniest.flx create mode 100644 build/tmp2/a.flx create mode 100644 build/tmp2/b.flx create mode 100644 build/tmp2/c.flx create mode 100644 notes.md delete mode 100755 repro_1 diff --git a/.gitignore b/.gitignore index df09c0c7..cb87e3a3 100644 --- a/.gitignore +++ b/.gitignore @@ -101,4 +101,5 @@ flax.includes build/__pycache__ *.vspx +.cache compile_flags.txt diff --git a/build/tiniest.flx b/build/tiniest.flx new file mode 100644 index 00000000..bc82d0fb --- /dev/null +++ b/build/tiniest.flx @@ -0,0 +1,24 @@ +// tiniest.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +import libc + +class wrapper +{ + init() + { + } + + static fn method(a: T, b: U) + { + libc::printf("t = %d, u = %d\n", typeid(a), typeid(b)); + } +} + + +@entry fn main() +{ + wrapper::method(1, "asdf") + libc::printf("hello, world\n") +} diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index acca60ac..63d39a4c 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -3,14 +3,14 @@ // Licensed under the Apache License Version 2.0. import libc as _ -import repro_2 +import repro_2 as _ -using repro_2 as _ +// using repro_2 as _ @entry fn main() { - // printf("four = %d\n", repro_2::Foo::FOUR.value); - printf("%d\n", Bar::bla(3)); + printf("four = %d\n", Foo::FOUR.value); + // printf("%d\n", Bar::bla(3)); // printf("lmao\n"); } diff --git a/build/tmp2/a.flx b/build/tmp2/a.flx new file mode 100644 index 00000000..f73de563 --- /dev/null +++ b/build/tmp2/a.flx @@ -0,0 +1,12 @@ +// a.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +import b as _ + + +@entry fn main() +{ + let x = bazzle() * 2 + foozle(x) +} diff --git a/build/tmp2/b.flx b/build/tmp2/b.flx new file mode 100644 index 00000000..a2777ca0 --- /dev/null +++ b/build/tmp2/b.flx @@ -0,0 +1,7 @@ +// b.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +public import c as _ + +public fn bazzle() -> int => 30 diff --git a/build/tmp2/c.flx b/build/tmp2/c.flx new file mode 100644 index 00000000..7e7bd296 --- /dev/null +++ b/build/tmp2/c.flx @@ -0,0 +1,12 @@ +// c.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +export c + +ffi fn printf(fmt: &i8, ...) -> int + +public fn foozle(x: int) +{ + printf("hello, world! (%d)\n", x) +} diff --git a/build/ultratiny.flx b/build/ultratiny.flx index 86000216..c9d3f9c1 100644 --- a/build/ultratiny.flx +++ b/build/ultratiny.flx @@ -23,16 +23,16 @@ class Foo : Foo1, Drop, Copy, Move { var data: int - init(x: int) + init(x: int) : super() { printf("of make (%d)\n", x) this.data = x } - fn deinit() - { - printf("is kill (%d)\n", data) - } + // fn deinit() + // { + // printf("is kill (%d)\n", data) + // } fn copy(other: &self) { diff --git a/notes.md b/notes.md new file mode 100644 index 00000000..1ee09a19 --- /dev/null +++ b/notes.md @@ -0,0 +1,19 @@ +# notes + + +each scope already exists as a StateTree, so, whenever a public definition is encountered, it is added to the +`exports` list of its tree. + +when importing another module: +1. if the module is imported `as _`, add the module's StateTree to the current (toplevel) scope's `imports` list +2. if the module is imported `as foo::bar`, add the module's StateTree to the `imports` list of `foo::bar` (creating if needed) + +3. do a (complete) tree traversal "in-step" with the current scope, to check for duplicate (incompatbile) definitions. + - big oof, but if we want the current module "paradigm" to work, this has to be done. + +when resolving a definition: +1. follow the same resolution order (deepest-to-widest) +2. after checking each level, additionally check the list of imports. +3. of course, we should not traverse "down" into the imported tree -- only look at its top-level defs + - we need to be going up, not down! +4. for each imported tree, we should only check definitions that are in its `exports` list diff --git a/repro_1 b/repro_1 deleted file mode 100755 index ef072862080d2dc91190d0582572fcf9a6a74dc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12700 zcmeHN&ud&&6uxg7+i4A%DRxnRL>>*1v`UgTrF3B-6O%ZhrA?C{t((J4=A{{!A7SQA zOV4RJ>raE z0g;utJ1hM|?0hbq6z3xqLp|r6_eBbiKF``=!gKN|RAjcW zQjqs<-CT}N3(iB|If>7UYl%ta7950KpRxl}<7vHB{&xM)S9yH3!b+YkO zdBL}m$}8qjq*jvlR=wP;$wt^Z6mLH-;3f0IXSMHrh*U~pDO!@xbbeVs$}h&FpTy&w zr1U=THx_PQpPOBX*f(Y}wNLnz&mBo@oN4>d8MAGN;K8-CXwTY6^zh;5VNRl5L`!}c ze+ZxB&s>C!_GKvJ6y_Z(-%rdbV`<4^{)_gBuLI*l2cZ+4c59+mEl*UoY8CjD*P+bg z`rYqd`Sz=8-h=UP{`~Bdhn-94^PzT+xl?8UO53h+{GH4N*m-CyW7xmlFV1^Xxb9Ub zW6a;Hc=&qrdbuE<<*jxY)W>JWkyo{hL6Eng#3!lb=e+aDmx?l=3@8K2fHI&AC-6zE9j{c5|*Nw-+ zf1oKWcSnCg=gvAqPyWJ;(aI^9ybHd;K1j0V9gn|3FQcW7xceY&E;@+k+2|IT0lw#> z4>!XM?uY}pBMzLgJ^~*v8E-T1cd!`Op!&#w!aHG77<(6*kHTGhZ%qzRTNzLW zlmTTx8Bhk40cAiLPzIC%Wk4BF2968^SKa?-kIaU3qso9XpbRJj%78MU3@8K2fHI&A zCh`G&epPptBtSyF@GU~%oynef7c1#-)*=SW8;Tk-MC#c8Ih{@MW)rMIx znfkf}o55-)3{dr6;>79s`Bt#eYRaXlnNn-2jEd*^f=pGfTsE_;hd5?-hw<2c6ZY4CNYE}g@0WAehqy?C~A-E+wapqiP~2`s9d}gwbg8WR8b$k1(Ab07E4CV(*Y($WaM70SuWL3c3%x;t realScope; + + sst::Scope enclosingScope; }; diff --git a/source/include/sst.h b/source/include/sst.h index a0031e05..6979f0b1 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -57,6 +57,7 @@ namespace sst ~TypeDefn() { } ast::TypeDefn* original = 0; + Scope innerScope; }; diff --git a/source/include/sst_expr.h b/source/include/sst_expr.h index 58ff7a64..aa72e00d 100644 --- a/source/include/sst_expr.h +++ b/source/include/sst_expr.h @@ -47,6 +47,7 @@ namespace sst bool global = false; std::string bareName; VisibilityLevel visibility = VisibilityLevel::Internal; + Scope enclosingScope; virtual std::string getKind() = 0; }; diff --git a/source/include/stcommon.h b/source/include/stcommon.h index 14128dfc..d8b4ef2e 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -8,6 +8,19 @@ namespace sst { struct VarDefn; + struct StateTree; + + struct Scope + { + Scope() { } + Scope(StateTree* st); + + StateTree* stree = 0; + const Scope* prev = 0; + + std::vector getStrings() const; + const Scope& appending(const std::string& name) const; + }; } namespace ast diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 421d0154..ec9de59e 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -46,17 +46,6 @@ namespace sst struct Solution_t; } - struct Scope - { - Scope() { } - Scope(StateTree* st); - - StateTree* stree = 0; - const Scope* prev = 0; - - std::vector getStrings() const; - }; - struct StateTree { StateTree(const std::string& nm, const std::string& filename, StateTree* p, bool anon = false) @@ -111,6 +100,7 @@ namespace sst std::vector getScope(); StateTree* searchForName(const std::string& name); + StateTree* findOrCreateSubtree(const std::string& name, bool anonymous = false); util::hash_map> getAllDefinitions(); @@ -218,6 +208,13 @@ namespace sst [[deprecated]] StateTree* getTreeOfScope(const std::vector& scope); + Scope getCurrentScope2(); + + std::vector teleportationStack; + void teleportInto(const Scope& scope); + void teleportOut(); + + std::vector getDefinitionsWithName(const std::string& name, StateTree* tree = 0); ErrorMsg* checkForShadowingOrConflictingDefinition(Defn* def, diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index f3993391..eda9d638 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -124,7 +124,10 @@ namespace repl // note: usually, visitDeclarables in the top-level typecheck will set the realScope. // BUT, since we're not doing that, we must set it manually! if(auto def = dcast(ast::Parameterisable, stmt); def) + { def->realScope = state->fs->getCurrentScope(); + def->enclosingScope = state->fs->getCurrentScope2(); + } tcr = stmt->typecheck(state->fs); } diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 3309fd87..eec5e75d 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -706,6 +706,63 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d +static sst::Expr* checkRhs2(sst::TypecheckState* fs, ast::DotOperator* dot, const sst::Scope& olds, const sst::Scope& news, + fir::Type* rhs_infer, sst::StructDefn* possibleStructDefn = 0) +{ + if(auto id = dcast(ast::Ident, dot->right)) + id->traverseUpwards = false; + + else if(auto fc = dcast(ast::FunctionCall, dot->right)) + fc->traverseUpwards = false; + + // note: for function/expr calls, we typecheck the arguments *before* we teleport to the scope, so that we don't conflate + // the scope of the argument (which is the current scope) with the scope of the call target (which is in whatever namespace) + + sst::Expr* ret = 0; + if(auto fc = dcast(ast::FunctionCall, dot->right)) + { + auto args = zfu::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, + e.second->typecheck(fs).expr(), e.second); + }); + + fs->teleportInto(news); + ret = fc->typecheckWithArguments(fs, args, rhs_infer); + } + else if(auto ec = dcast(ast::ExprCall, dot->right)) + { + auto args = zfu::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, + e.second->typecheck(fs).expr(), e.second); + }); + + fs->teleportInto(news); + ret = ec->typecheckWithArguments(fs, args, rhs_infer); + } + else if(dcast(ast::Ident, dot->right) || dcast(ast::DotOperator, dot->right)) + { + fs->teleportInto(news); + auto res = dot->right->typecheck(fs, rhs_infer); + if(res.isError() && possibleStructDefn && dcast(ast::Ident, dot->right)) + { + wrongDotOpError(res.error(), possibleStructDefn, dot->right->loc, dcast(ast::Ident, dot->right)->name, true)->postAndQuit(); + } + else + { + // will post if res is an error, even if we didn't give the fancy error. + ret = res.expr(); + } + } + else + { + error(dot->right, "unexpected %s on right-side of dot-operator following static scope '%s' on the left", dot->right->readableName, + zfu::join(news.getStrings(), "::")); + } + + iceAssert(ret); + + // fs->teleportToScope(olds); + fs->teleportOut(); + return ret; +} @@ -816,7 +873,6 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, if(dcast(sst::ClassDefn, def) || dcast(sst::StructDefn, def)) { fs->pushSelfContext(def->type); - defer(fs->popSelfContext()); auto oldscope = fs->getCurrentScope(); auto newscope = typdef->id.scope; @@ -832,7 +888,10 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, fs->addGenericMapping(g.first, fir::PolyPlaceholderType::get(g.first, pses)); } - return checkRhs(fs, dot, oldscope, newscope, infer); + auto ret = checkRhs(fs, dot, oldscope, newscope, infer); + fs->popSelfContext(); + + return ret; } else if(auto unn = dcast(sst::UnionDefn, def)) { @@ -885,11 +944,12 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } else if(auto enm = dcast(sst::EnumDefn, def)) { - auto oldscope = fs->getCurrentScope(); - auto newscope = enm->id.scope; - newscope.push_back(enm->id.name); + auto oldscope = fs->getCurrentScope2(); + auto newscope = enm->innerScope; + // auto newscope = enm->id.scope; + // newscope.push_back(enm->id.name); - auto rhs = checkRhs(fs, dot, oldscope, newscope, infer); + auto rhs = checkRhs2(fs, dot, oldscope, newscope, infer); if(auto vr = dcast(sst::VarRef, rhs)) { diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index 453373bc..563a0d64 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -43,6 +43,8 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->id.scope = this->realScope; defn->visibility = this->visibility; defn->original = this; + defn->enclosingScope = this->enclosingScope; + defn->innerScope = this->enclosingScope.appending(defnname); // set it to void first, because we want to defer typechecking the member type. defn->type = fir::EnumType::get(defn->id, fir::Type::getVoid()); @@ -50,7 +52,8 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) return TCResult(err); - fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + // fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + this->enclosingScope.stree->addDefinition(defnname, defn, gmaps); this->genericVersions.push_back({ defn, fs->getGenericContextStack() }); return TCResult(defn); @@ -69,9 +72,7 @@ TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con auto defn = dcast(sst::EnumDefn, tcr.defn()); iceAssert(defn); - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(defn->id.scope); - fs->pushTree(defn->id.name); + fs->teleportInto(defn->innerScope); if(this->memberType) defn->memberType = fs->convertParserTypeToFIR(this->memberType); else defn->memberType = fir::Type::getNativeWord(); @@ -107,9 +108,7 @@ TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con defn->type = ety; - fs->popTree(); - fs->teleportToScope(oldscope); - + fs->teleportOut(); return TCResult(defn); } diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index bc499cce..790b7119 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -11,87 +11,9 @@ #include #include "memorypool.h" -static void _checkFieldRecursion(sst::TypecheckState* fs, fir::Type* strty, fir::Type* field, const Location& floc, std::set& seeing) -{ - seeing.insert(strty); - - if(field == strty) - { - SimpleError::make(floc, "composite type '%s' cannot contain a field of its own type; use a pointer.", strty) - ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "type '%s' was defined here:", strty)) - ->postAndQuit(); - } - else if(seeing.find(field) != seeing.end()) - { - SimpleError::make(floc, "recursive definition of field with a non-pointer type; mutual recursion between types '%s' and '%s'", field, strty) - ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "type '%s' was defined here:", strty)) - ->postAndQuit(); - } - else if(field->isClassType()) - { - for(auto f : field->toClassType()->getElements()) - _checkFieldRecursion(fs, field, f, floc, seeing); - } - else if(field->isStructType()) - { - for(auto f : field->toStructType()->getElements()) - _checkFieldRecursion(fs, field, f, floc, seeing); - } - else if(field->isRawUnionType()) - { - for(auto f : field->toRawUnionType()->getVariants()) - _checkFieldRecursion(fs, field, f.second, floc, seeing); - } - - // ok, we should be fine...? -} - +static void _checkFieldRecursion(sst::TypecheckState* fs, fir::Type* strty, fir::Type* field, const Location& floc, std::set& seeing); static void _checkTransparentFieldRedefinition(sst::TypecheckState* fs, sst::TypeDefn* defn, const std::vector& fields, - util::hash_map& seen) -{ - for(auto fld : fields) - { - if(fld->isTransparentField) - { - auto ty = fld->type; - if(!ty->isRawUnionType() && !ty->isStructType()) - { - // you can't have a transparentl field if it's not an aggregate type, lmao - error(fld, "transparent fields must have either a struct or raw-union type."); - } - - auto innerdef = fs->typeDefnMap[ty]; - iceAssert(innerdef); - - std::vector flds; - if(auto str = dcast(sst::StructDefn, innerdef); str) - flds = str->fields; - - else if(auto unn = dcast(sst::RawUnionDefn, innerdef); unn) - flds = zfu::map(unn->fields, zfu::pair_second()) + unn->transparentFields; - - else - error(fs->loc(), "what kind of type is this? '%s'", ty); - - _checkTransparentFieldRedefinition(fs, innerdef, flds, seen); - } - else - { - if(auto it = seen.find(fld->id.name); it != seen.end()) - { - SimpleError::make(fld->loc, "redefinition of transparently accessible field '%s'", fld->id.name) - ->append(SimpleError::make(MsgType::Note, it->second, "previous definition was here:")) - ->postAndQuit(); - } - else - { - seen[fld->id.name] = fld->loc; - } - } - } -} - - + util::hash_map& seen); // used in typecheck/unions.cpp and typecheck/classes.cpp void checkFieldRecursion(sst::TypecheckState* fs, fir::Type* strty, fir::Type* field, const Location& floc) @@ -106,8 +28,6 @@ void checkTransparentFieldRedefinition(sst::TypecheckState* fs, sst::TypeDefn* d _checkTransparentFieldRedefinition(fs, defn, fields, seen); } - - // defined in typecheck/traits.cpp void checkTraitConformity(sst::TypecheckState* fs, sst::TypeDefn* defn); @@ -268,6 +188,85 @@ TCResult ast::StructDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, c +static void _checkFieldRecursion(sst::TypecheckState* fs, fir::Type* strty, fir::Type* field, const Location& floc, std::set& seeing) +{ + seeing.insert(strty); + + if(field == strty) + { + SimpleError::make(floc, "composite type '%s' cannot contain a field of its own type; use a pointer.", strty) + ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "type '%s' was defined here:", strty)) + ->postAndQuit(); + } + else if(seeing.find(field) != seeing.end()) + { + SimpleError::make(floc, "recursive definition of field with a non-pointer type; mutual recursion between types '%s' and '%s'", field, strty) + ->append(SimpleError::make(MsgType::Note, fs->typeDefnMap[strty]->loc, "type '%s' was defined here:", strty)) + ->postAndQuit(); + } + else if(field->isClassType()) + { + for(auto f : field->toClassType()->getElements()) + _checkFieldRecursion(fs, field, f, floc, seeing); + } + else if(field->isStructType()) + { + for(auto f : field->toStructType()->getElements()) + _checkFieldRecursion(fs, field, f, floc, seeing); + } + else if(field->isRawUnionType()) + { + for(auto f : field->toRawUnionType()->getVariants()) + _checkFieldRecursion(fs, field, f.second, floc, seeing); + } + + // ok, we should be fine...? +} + +static void _checkTransparentFieldRedefinition(sst::TypecheckState* fs, sst::TypeDefn* defn, const std::vector& fields, + util::hash_map& seen) +{ + for(auto fld : fields) + { + if(fld->isTransparentField) + { + auto ty = fld->type; + if(!ty->isRawUnionType() && !ty->isStructType()) + { + // you can't have a transparentl field if it's not an aggregate type, lmao + error(fld, "transparent fields must have either a struct or raw-union type."); + } + + auto innerdef = fs->typeDefnMap[ty]; + iceAssert(innerdef); + + std::vector flds; + if(auto str = dcast(sst::StructDefn, innerdef); str) + flds = str->fields; + + else if(auto unn = dcast(sst::RawUnionDefn, innerdef); unn) + flds = zfu::map(unn->fields, zfu::pair_second()) + unn->transparentFields; + + else + error(fs->loc(), "what kind of type is this? '%s'", ty); + + _checkTransparentFieldRedefinition(fs, innerdef, flds, seen); + } + else + { + if(auto it = seen.find(fld->id.name); it != seen.end()) + { + SimpleError::make(fld->loc, "redefinition of transparently accessible field '%s'", fld->id.name) + ->append(SimpleError::make(MsgType::Note, it->second, "previous definition was here:")) + ->postAndQuit(); + } + else + { + seen[fld->id.name] = fld->loc; + } + } + } +} diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 203dfc6a..41851e5a 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -429,6 +429,7 @@ static void visitDeclarables(sst::TypecheckState* fs, ast::TopLevelBlock* top) if(auto decl = dcast(ast::Parameterisable, stmt)) { decl->realScope = fs->getCurrentScope(); + decl->enclosingScope = fs->getCurrentScope2(); decl->generateDeclaration(fs, 0, { }); } diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index be2d6953..0846b248 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -32,7 +32,12 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* // make all our methods be methods for(auto m : this->methods) - m->parentType = this, m->realScope = this->realScope + defn->id.name; + { + m->parentType = this; + m->realScope = this->realScope + defn->id.name; + // m->enclosingScope = + } + auto str = fir::TraitType::create(defn->id); defn->type = str; diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 3d1dbaf0..93e21577 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -138,26 +138,10 @@ namespace sst - void TypecheckState::pushTree(const std::string& name, bool createAnonymously) { iceAssert(this->stree); - - if(auto it = this->stree->subtrees.find(name); it != this->stree->subtrees.end()) - { - this->stree = it->second; - } - else - { - auto newtree = util::pool(name, this->stree->topLevelFilename, this->stree, createAnonymously); - this->stree->subtrees[name] = newtree; - - // make a treedef. - newtree->treeDefn = util::pool(Location()); - newtree->treeDefn->tree = newtree; - - this->stree = newtree; - } + this->stree = this->stree->findOrCreateSubtree(name, createAnonymously); } StateTree* TypecheckState::popTree() @@ -201,6 +185,11 @@ namespace sst return this->deferBlockNest > 0; } + Scope TypecheckState::getCurrentScope2() + { + return this->stree->getScope2(); + } + std::string TypecheckState::serialiseCurrentScope() { std::deque scope; @@ -364,6 +353,14 @@ namespace sst this->addDefinition(this->topLevelFilename, _name, def, gmaps); } + + + + + + + + std::vector Scope::getStrings() const { std::vector ret; @@ -392,6 +389,31 @@ namespace sst return this->cachedScope; } + const Scope& Scope::appending(const std::string& name) const + { + return this->stree->findOrCreateSubtree(name)->getScope2(); + } + + void TypecheckState::teleportInto(const Scope& scope) + { + this->teleportationStack.push_back(this->stree); + this->stree = scope.stree; + } + + void TypecheckState::teleportOut() + { + this->stree = this->teleportationStack.back(); + this->teleportationStack.pop_back(); + } + + + + + + + + + // TODO: maybe cache this someday? std::vector StateTree::getScope() { @@ -422,7 +444,24 @@ namespace sst return 0; } + StateTree* StateTree::findOrCreateSubtree(const std::string& name, bool anonymous) + { + if(auto it = this->subtrees.find(name); it != this->subtrees.end()) + { + return it->second; + } + else + { + auto newtree = util::pool(name, this->topLevelFilename, this, anonymous); + this->subtrees[name] = newtree; + // make a treedef. + newtree->treeDefn = util::pool(Location()); + newtree->treeDefn->tree = newtree; + + return newtree; + } + } From 3424cb2a3d08a711a445bd19290d769a78f9d4d2 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 28 Nov 2020 15:10:23 +0800 Subject: [PATCH 111/129] fix classes as well, and update gha CI to llvm 11 --- .github/workflows/continuous_integration.yml | 17 ++--- build/tmp/repro_1.flx | 26 ++++++- issues.md | 2 +- notes.md | 59 +++++++++++++++- source/include/sst.h | 1 + source/include/typecheck.h | 1 + source/typecheck/classes.cpp | 57 +++++++++++---- source/typecheck/dotop.cpp | 14 ++-- source/typecheck/enums.cpp | 2 +- source/typecheck/function.cpp | 6 ++ source/typecheck/using.cpp | 73 +++++++++++++++++--- 11 files changed, 217 insertions(+), 41 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index ced5de46..69b4367e 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -8,13 +8,13 @@ jobs: - uses: actions/checkout@v1 - name: install dependencies run: | - sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list + sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main" | sudo tee -a /etc/apt/sources.list sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt -y update - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y g++-9 llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 + sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y g++-9 llvm-11 llvm-11-dev libllvm11 libmpfr-dev libmpfr6 - name: build env: - LLVM_CONFIG: llvm-config-9 + LLVM_CONFIG: llvm-config-11 CC: gcc-9 CXX: g++-9 run: make -j2 build @@ -24,9 +24,9 @@ jobs: run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - name: compile/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester - + build-macos: - runs-on: macOS-10.14 + runs-on: macOS-10.15 steps: - uses: actions/checkout@v1 - name: install dependencies @@ -35,14 +35,14 @@ jobs: - name: build env: LLVM_CONFIG: /usr/local/opt/llvm/bin/llvm-config - run: PATH="$PATH:$(pwd)/llvm/9.0.0/bin" make -j2 build + run: PATH="$PATH:$(pwd)/llvm/11.0.0/bin" make -j2 build - name: jit/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - name: jit/interp run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - name: compile/llvm run: build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester - + build-windows: runs-on: windows-2019 env: @@ -50,6 +50,7 @@ jobs: MPFR_ROOT_DIR: C:/tmp/lib/mpfr LLVM_ROOT_DIR: C:/tmp/lib/llvm LIBFFI_ROOT_DIR: C:/tmp/lib/libffi + ACTIONS_ALLOW_UNSECURE_COMMANDS: true steps: - uses: seanmiddleditch/gha-setup-ninja@v1 - uses: seanmiddleditch/gha-setup-vsdevenv@master @@ -85,7 +86,7 @@ jobs: run: | build\meson-rel\flaxc.exe -sysroot build\sysroot --ffi-escape build\tester.flx .\tester.exe - + diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index 63d39a4c..e69c436a 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -9,8 +9,30 @@ import repro_2 as _ @entry fn main() { + class Foozle + { + init() { } + + static fn alpha() { } + + class Flub + { + init() { } + + static fn beta() { } + + static fn omega() { printf("owo\n") } + }; + } + printf("four = %d\n", Foo::FOUR.value); - // printf("%d\n", Bar::bla(3)); - // printf("lmao\n"); + + using Foozle::Flub as Flubzle + Flubzle::omega(); + + printf("%d\n", Bar::bla(3)); + printf("lmao\n"); + + // let c = "a" + "b" } diff --git a/issues.md b/issues.md index 72dff49b..149ddc67 100644 --- a/issues.md +++ b/issues.md @@ -112,7 +112,7 @@ Note: this is just a personal log of outstanding issues, shorter rants/ramblings `fn foo(a: T) -> T => a * a` The type inference would require some re-working though, because to generate the declaration of the function we need the return type, but to - get the return type in this situation we need to typecheck the function body. Albeit it's a single function, there might still be + get the return type in this situation we need to typecheck the function body. Albeit it's a single expression, there might still be unresolved or unresolvable things inside the body. Possibly investigate whether we can do the typechecking in the generateDecl function?? But it's highly likely we separated those for a reason, diff --git a/notes.md b/notes.md index 1ee09a19..0046973c 100644 --- a/notes.md +++ b/notes.md @@ -1,5 +1,4 @@ -# notes - +## notes each scope already exists as a StateTree, so, whenever a public definition is encountered, it is added to the `exports` list of its tree. @@ -11,9 +10,63 @@ when importing another module: 3. do a (complete) tree traversal "in-step" with the current scope, to check for duplicate (incompatbile) definitions. - big oof, but if we want the current module "paradigm" to work, this has to be done. -when resolving a definition: +(done) when resolving a definition: 1. follow the same resolution order (deepest-to-widest) 2. after checking each level, additionally check the list of imports. 3. of course, we should not traverse "down" into the imported tree -- only look at its top-level defs - we need to be going up, not down! 4. for each imported tree, we should only check definitions that are in its `exports` list + +to refactor using: +we should simply treat it as a scope-level import, ie. for `using foo::bar as qux`, we just create a new scope +`qux`, then attach `foo::bar` to `qux` by appending to its `imports` list. here, we should check that `qux` does +not already exist. +(this is different for imports, where two imported modules are able merge their exported namespaces. for `using`, +i'm not too sure that's a good idea, but it's easy enough to change if needed) + +for `using foo::bar as _`, we do the same thing as `import as _`, and just attach the tree of `foo::bar` to +the imports list of the current scope. + + + +## to refactor + +1. we should probably get rid of TreeDefn + - a lot of places are "forced" to setup a TreeDefn when they setup a new StateTree scope + - from a cursory inspection, it's only used for "concretising" the interim parts of `a::b::c`, so we + can parse it as `(a::b)::c` and then perform resolution *as-if* it is a normal scope definition + - i'm sure there's a way to refactor this... hopefully. + +2. a lot of places probably still have the concept of `scope == std::vector`. + - after the first scope refactor, i think these instances will be reduced + - undoubtedly there will be more. for instance, Identifier holds the scope as exactly that. + (but, is there actually a need for it to be anything else? it really is just an identifier, after all) + +3. all uses of defer() need to be audited. there are instances where an exception is thrown during typechecking + as a result of unwrapping a `TCResult` that contains an error; as the stack unwinds, it may encounter a + defer() object, which necessitates calling the code inside. + + - in general that is fine (most of the time, it's just `fs->pushLoc()` followed by `defer(fs->popLoc())`), + but in some instances it is not fine --- most notably, when we push a body context and pop it in the defer. + + - since the popping of the body context asserts that the top of the stack is the same as what we expect, we might + end up in a situation where exception-throwing function pushes a different context and throws before popping it, + leaving the calling frame with an inconsistent view + + - the assertion will cause the compiler to terminate when it should have just printed the error message generated + by the exception-throwing function. + +4. instead of keeping a separate list of `unresolvedGenericDefns` which is pretty ugly, it should be feasible to create + a sort of `sst::ParametricDefn` that simply contains a pointer to the original `ast::Parameterisable` as its only + field; then, we should be able to simplify the typechecking code substantially by moving those the polymorph + instantiation into that definition's typecheck method instead. + +5. the definitions map should probably just get rid of extra level of mapping from file source to list, since i think + that it's only used for the scuffed addTreeToExistingTree thing. + + + +## to investigate + +1. we rely on a lot of places to set `realScope` (and `enclosingScope`) correctly when typechecking structs etc. there should + be a better way to do this, probably automatically or something like that. basically anything except error-prone manual setting. diff --git a/source/include/sst.h b/source/include/sst.h index 6979f0b1..eca0c752 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -407,6 +407,7 @@ namespace sst virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; std::vector scope; + Scope scope2; }; struct FieldDotOp : Expr diff --git a/source/include/typecheck.h b/source/include/typecheck.h index ec9de59e..a0fc5601 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -209,6 +209,7 @@ namespace sst StateTree* getTreeOfScope(const std::vector& scope); Scope getCurrentScope2(); + // StateTree* getTree std::vector teleportationStack; void teleportInto(const Scope& scope); diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index 49529f0d..84170ec2 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -39,11 +39,30 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->id.scope = this->realScope; defn->visibility = this->visibility; defn->original = this; + defn->enclosingScope = this->enclosingScope; + defn->innerScope = this->enclosingScope.appending(defnname); + // make all our methods be methods - for(auto m : this->methods) { m->parentType = this; m->realScope = this->realScope + defn->id.name; } - for(auto m : this->initialisers) { m->parentType = this; m->realScope = this->realScope + defn->id.name; } - for(auto m : this->staticMethods) { m->realScope = this->realScope + defn->id.name; } + for(auto m : this->methods) + { + m->parentType = this; + m->realScope = this->realScope + defn->id.name; + m->enclosingScope = defn->innerScope; + } + + for(auto m : this->initialisers) + { + m->parentType = this; + m->realScope = this->realScope + defn->id.name; + m->enclosingScope = defn->innerScope; + } + + for(auto m : this->staticMethods) + { + m->realScope = this->realScope + defn->id.name; + m->enclosingScope = defn->innerScope; + } auto cls = fir::ClassType::createWithoutBody(defn->id); @@ -89,22 +108,27 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* // add it first so we can use it in the method bodies, // and make pointers to it { - fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + // fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + defn->enclosingScope.stree->addDefinition(defnname, defn, gmaps); fs->typeDefnMap[cls] = defn; } - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(defn->id.scope); - fs->pushTree(defn->id.name); + // auto oldscope = fs->getCurrentScope(); + // fs->teleportToScope(defn->id.scope); + // fs->pushTree(defn->id.name); + fs->teleportInto(defn->innerScope); { for(auto t : this->nestedTypes) { t->realScope = this->realScope + defn->id.name; + t->enclosingScope = defn->innerScope; t->generateDeclaration(fs, 0, { }); } } - fs->popTree(); - fs->teleportToScope(oldscope); + fs->teleportOut(); + + // fs->popTree(); + // fs->teleportToScope(oldscope); this->genericVersions.push_back({ defn, fs->getGenericContextStack() }); @@ -129,9 +153,11 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co auto cls = defn->type->toClassType(); iceAssert(cls); - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(defn->id.scope); - fs->pushTree(defn->id.name); + // auto oldscope = fs->getCurrentScope(); + // fs->teleportToScope(defn->id.scope); + // fs->pushTree(defn->id.name); + + fs->teleportInto(defn->innerScope); if(this->initialisers.empty()) error(this, "class must have at least one initialiser"); @@ -408,8 +434,10 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co fs->popSelfContext(); - fs->popTree(); - fs->teleportToScope(oldscope); + fs->teleportOut(); + + // fs->popTree(); + // fs->teleportToScope(oldscope); this->finishedTypechecking.insert(defn); return TCResult(defn); @@ -506,6 +534,7 @@ TCResult ast::InitFunctionDefn::generateDeclaration(sst::TypecheckState* fs, fir this->actualDefn->returnType = pts::NamedType::create(this->loc, VOID_TYPE_STRING); this->actualDefn->realScope = this->realScope; + this->actualDefn->enclosingScope = this->enclosingScope; //* note: constructors will always mutate, definitely. this->actualDefn->isMutating = true; diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index eec5e75d..65bb9a0e 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -860,6 +860,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, newscope.push_back(vr->name); auto ret = util::pool(dot->loc, fir::Type::getVoid()); ret->scope = newscope; + ret->scope2 = td->tree->getScope2().appending(vr->name); return ret; } @@ -874,9 +875,12 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, { fs->pushSelfContext(def->type); - auto oldscope = fs->getCurrentScope(); - auto newscope = typdef->id.scope; - newscope.push_back(typdef->id.name); + auto oldscope = fs->getCurrentScope2(); + auto newscope = typdef->innerScope; + + // auto oldscope = fs->getCurrentScope(); + // auto newscope = typdef->id.scope; + // newscope.push_back(typdef->id.name); fs->pushGenericContext(); defer(fs->popGenericContext()); @@ -888,7 +892,8 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, fs->addGenericMapping(g.first, fir::PolyPlaceholderType::get(g.first, pses)); } - auto ret = checkRhs(fs, dot, oldscope, newscope, infer); + // auto ret = checkRhs(fs, dot, oldscope, newscope, infer); + auto ret = checkRhs2(fs, dot, oldscope, newscope, infer); fs->popSelfContext(); return ret; @@ -983,6 +988,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, newscope.push_back(vr->name); auto ret = util::pool(dot->loc, fir::Type::getVoid()); ret->scope = newscope; + ret->scope2 = scp->scope2.appending(vr->name); return ret; } diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index 563a0d64..c770dcce 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -53,7 +53,7 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* return TCResult(err); // fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); - this->enclosingScope.stree->addDefinition(defnname, defn, gmaps); + defn->enclosingScope.stree->addDefinition(defnname, defn, gmaps); this->genericVersions.push_back({ defn, fs->getGenericContextStack() }); return TCResult(defn); diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 98a33e77..188780c0 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -310,7 +310,10 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) for(auto stmt : this->statements) { if(auto p = dcast(Parameterisable, stmt); p) + { p->realScope = fs->getCurrentScope(); + p->enclosingScope = fs->getCurrentScope2(); + } auto tcr = stmt->typecheck(fs); if(tcr.isError()) @@ -323,7 +326,10 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) for(auto dstmt : this->deferredStatements) { if(auto p = dcast(Parameterisable, dstmt); p) + { p->realScope = fs->getCurrentScope(); + p->enclosingScope = fs->getCurrentScope2(); + } auto tcr = dstmt->typecheck(fs); if(tcr.isError()) diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index 5a0f4448..d6612031 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -3,7 +3,9 @@ // Licensed under the Apache License Version 2.0. #include "ast.h" +#include "defs.h" #include "errors.h" +#include "sst.h" #include "typecheck.h" #include "ir/type.h" @@ -36,6 +38,32 @@ static void importScopeContentsIntoNewScope(sst::TypecheckState* fs, const std:: } +static void importScopeContentsIntoNewScope2(sst::TypecheckState* fs, const std::vector& sfrom, + const std::vector& stoParent, const std::string& name) +{ + auto parent = fs->getTreeOfScope(stoParent); + + if(auto defs = parent->getDefinitionsWithName(name); !defs.empty()) + { + auto err = SimpleError::make(fs->loc(), + "cannot use import scope '%s' into scope '%s' with name '%s'; one or more conflicting definitions exist", + zfu::join(sfrom, "::"), zfu::join(stoParent, "::"), + name); + + for(const auto& d : defs) + err->append(SimpleError::make(MsgType::Note, d->loc, "conflicting definition here:")); + + err->postAndQuit(); + } + + // add a thing in the current scope + auto treedef = util::pool(fs->loc()); + treedef->tree = fs->getTreeOfScope(sfrom); + treedef->tree->treeDefn = treedef; + + parent->addDefinition(name, treedef); +} + TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) { @@ -43,8 +71,8 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) defer(fs->popLoc()); // check what kind of expression we have. - auto user = this->expr->typecheck(fs).expr(); - if(!dcast(sst::ScopeExpr, user) && !dcast(sst::VarRef, user)) + auto used = this->expr->typecheck(fs).expr(); + if(!dcast(sst::ScopeExpr, used) && !dcast(sst::VarRef, used)) error(this->expr, "unsupported expression on left-side of 'using' declaration"); @@ -54,13 +82,18 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) // and only getting ScopeExpr if it's really a namespace reference. + sst::Scope scopes2; std::vector scopes; - if(auto vr = dcast(sst::VarRef, user); vr && dcast(sst::EnumDefn, vr->def)) + auto vr = dcast(sst::VarRef, used); + + if(vr && dcast(sst::EnumDefn, vr->def)) { auto enrd = dcast(sst::EnumDefn, vr->def); scopes = enrd->id.scope; scopes.push_back(enrd->id.name); + + scopes2 = enrd->innerScope; } // uses the same 'vr' from the branch above else if(vr && dcast(sst::UnionDefn, vr->def)) @@ -69,15 +102,23 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) scopes = unn->id.scope; scopes.push_back(unn->id.name); + + scopes2 = unn->innerScope; } else { - if(auto se = dcast(sst::ScopeExpr, user)) - scopes = se->scope; + sst::TypeDefn* td = nullptr; + + // this happens in cases like `foo::bar` + if(auto se = dcast(sst::ScopeExpr, used)) + scopes = se->scope, scopes2 = se->scope2; - else if(auto vr = dcast(sst::VarRef, user)) - scopes = { vr->name }; + // and this happens in cases like `foo` + else if(vr && (td = dcast(sst::TypeDefn, vr->def))) + scopes = { vr->name }, scopes2 = td->innerScope; + else + error("what is this?"); } if(this->useAs == "_") @@ -89,7 +130,23 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) } else { - importScopeContentsIntoNewScope(fs, scopes, fs->getCurrentScope(), this->useAs); + // check that the current scope doesn't contain this thing + if(auto existing = fs->getDefinitionsWithName(this->useAs); existing.size() > 0) + { + auto err = SimpleError::make(fs->loc(), "cannot use scope '%s' as '%s'; one or more conflicting definitions exist", + zfu::join(scopes, "::"), this->useAs); + + err->append(SimpleError::make(MsgType::Note, existing[0]->loc, "first conflicting definition here:")); + err->postAndQuit(); + } + + auto treedef = util::pool(fs->loc()); + auto tree = fs->stree->findOrCreateSubtree(this->useAs); + treedef->tree = tree; + treedef->tree->treeDefn = treedef; + + tree->imports.push_back(scopes2.stree); + fs->stree->addDefinition(this->useAs, treedef); } return TCResult::getDummy(); From b43b2e8691530cfa61205a144a0ba3e89275e778 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 28 Nov 2020 16:28:04 +0800 Subject: [PATCH 112/129] work-saving --- build/tmp/repro_1.flx | 36 +++++++-------- source/typecheck/toplevel.cpp | 27 ++++++++--- source/typecheck/typecheckstate.cpp | 4 ++ source/typecheck/using.cpp | 72 ++++------------------------- source/typecheck/variable.cpp | 1 + 5 files changed, 53 insertions(+), 87 deletions(-) diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index e69c436a..a03a49a0 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -3,35 +3,35 @@ // Licensed under the Apache License Version 2.0. import libc as _ -import repro_2 as _ +import repro_2 -// using repro_2 as _ +using repro_2 as r @entry fn main() { - class Foozle - { - init() { } + // class Foozle + // { + // init() { } - static fn alpha() { } + // static fn alpha() { } - class Flub - { - init() { } + // class Flub + // { + // init() { } - static fn beta() { } + // static fn beta() { } - static fn omega() { printf("owo\n") } - }; - } + // static fn omega() { printf("owo\n") } + // }; + // } - printf("four = %d\n", Foo::FOUR.value); + printf("four = %d\n", r::Foo::FOUR.value); - using Foozle::Flub as Flubzle - Flubzle::omega(); + // using Foozle::Flub as Flubzle + // Flubzle::omega(); - printf("%d\n", Bar::bla(3)); - printf("lmao\n"); + // printf("%d\n", Bar::bla(3)); + // printf("lmao\n"); // let c = "a" + "b" } diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 41851e5a..9fe028ca 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. +#include "defs.h" #include "sst.h" #include "ast.h" #include "pts.h" @@ -222,6 +223,7 @@ namespace sst { StateTree* tree = new StateTree(file.moduleName, file.name, 0); tree->treeDefn = util::pool(Location()); + tree->treeDefn->enclosingScope = Scope(); tree->treeDefn->tree = tree; auto fs = new TypecheckState(tree); @@ -266,6 +268,10 @@ namespace sst auto treedef = util::pool(cs->dtrees[ithing.name]->topLevel->loc); treedef->id = Identifier(impas, IdKind::Name); treedef->tree = newinspt; + + // this is the parent scope! + treedef->enclosingScope = Scope(curinspt); + treedef->tree->treeDefn = treedef; treedef->visibility = ithing.pubImport ? VisibilityLevel::Public @@ -283,6 +289,19 @@ namespace sst iceAssert(insertPoint); insertPoint->imports.push_back(import->base); + { + // make a new treedef referring to the newly-imported tree. + auto treedef = util::pool(ithing.loc); + treedef->tree = import->base; + treedef->enclosingScope = sst::Scope(insertPoint); + + insertPoint->addDefinition(import->base->name, treedef); + + + zpr::println("import: using tree %p, defn = %p", import->base, treedef); + } + + if(ithing.pubImport) insertPoint->reexports.push_back(import->base); @@ -495,12 +514,8 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer if(tree->parent) { - auto td = util::pool(this->loc); - - td->tree = tree; - td->tree->treeDefn = td; - td->id = Identifier(this->name, IdKind::Name); - td->id.scope = tree->parent->getScope(); + auto td = tree->treeDefn; + iceAssert(td); td->visibility = this->visibility; diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 93e21577..68d56b30 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -457,6 +457,10 @@ namespace sst // make a treedef. newtree->treeDefn = util::pool(Location()); + + // this is the parent! + newtree->treeDefn->enclosingScope = Scope(this); + newtree->treeDefn->tree = newtree; return newtree; diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index d6612031..ddabc38c 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -11,59 +11,6 @@ #include "ir/type.h" #include "memorypool.h" -static void importScopeContentsIntoNewScope(sst::TypecheckState* fs, const std::vector& sfrom, - const std::vector& stoParent, const std::string& name) -{ - auto parent = fs->getTreeOfScope(stoParent); - - if(auto defs = parent->getDefinitionsWithName(name); !defs.empty()) - { - auto err = SimpleError::make(fs->loc(), - "cannot use import scope '%s' into scope '%s' with name '%s'; one or more conflicting definitions exist", - zfu::join(sfrom, "::"), zfu::join(stoParent, "::"), - name); - - for(const auto& d : defs) - err->append(SimpleError::make(MsgType::Note, d->loc, "conflicting definition here:")); - - err->postAndQuit(); - } - - // add a thing in the current scope - auto treedef = util::pool(fs->loc()); - treedef->tree = fs->getTreeOfScope(sfrom); - treedef->tree->treeDefn = treedef; - - parent->addDefinition(name, treedef); -} - - -static void importScopeContentsIntoNewScope2(sst::TypecheckState* fs, const std::vector& sfrom, - const std::vector& stoParent, const std::string& name) -{ - auto parent = fs->getTreeOfScope(stoParent); - - if(auto defs = parent->getDefinitionsWithName(name); !defs.empty()) - { - auto err = SimpleError::make(fs->loc(), - "cannot use import scope '%s' into scope '%s' with name '%s'; one or more conflicting definitions exist", - zfu::join(sfrom, "::"), zfu::join(stoParent, "::"), - name); - - for(const auto& d : defs) - err->append(SimpleError::make(MsgType::Note, d->loc, "conflicting definition here:")); - - err->postAndQuit(); - } - - // add a thing in the current scope - auto treedef = util::pool(fs->loc()); - treedef->tree = fs->getTreeOfScope(sfrom); - treedef->tree->treeDefn = treedef; - - parent->addDefinition(name, treedef); -} - TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) { @@ -108,6 +55,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) else { sst::TypeDefn* td = nullptr; + sst::TreeDefn* tr = nullptr; // this happens in cases like `foo::bar` if(auto se = dcast(sst::ScopeExpr, used)) @@ -117,36 +65,34 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) else if(vr && (td = dcast(sst::TypeDefn, vr->def))) scopes = { vr->name }, scopes2 = td->innerScope; + else if(vr && (tr = dcast(sst::TreeDefn, vr->def))) + scopes = { vr->name }, scopes2 = sst::Scope(tr->tree); + else - error("what is this?"); + error("unsupported LHS of using: '%s'", used->readableName); } if(this->useAs == "_") { - auto fromtree = fs->getTreeOfScope(scopes); - auto totree = fs->stree; - - sst::addTreeToExistingTree(totree, fromtree, totree->parent, /* pubImport: */ false, /* ignoreVis: */ true); + // TODO: check scope merge conflicts + fs->stree->imports.push_back(scopes2.stree); } else { // check that the current scope doesn't contain this thing if(auto existing = fs->getDefinitionsWithName(this->useAs); existing.size() > 0) { - auto err = SimpleError::make(fs->loc(), "cannot use scope '%s' as '%s'; one or more conflicting definitions exist", + auto err = SimpleError::make(this->loc, "cannot use scope '%s' as '%s'; one or more conflicting definitions exist", zfu::join(scopes, "::"), this->useAs); err->append(SimpleError::make(MsgType::Note, existing[0]->loc, "first conflicting definition here:")); err->postAndQuit(); } - auto treedef = util::pool(fs->loc()); auto tree = fs->stree->findOrCreateSubtree(this->useAs); - treedef->tree = tree; - treedef->tree->treeDefn = treedef; tree->imports.push_back(scopes2.stree); - fs->stree->addDefinition(this->useAs, treedef); + fs->stree->addDefinition(this->useAs, tree->treeDefn); } return TCResult::getDummy(); diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 1e2cfa7b..1185e3c6 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -289,6 +289,7 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) } // ok, we haven't found anything + fs->stree->dump(); return TCResult( SimpleError::make(this->loc, "reference to unknown entity '%s'", this->name) ); From 9a9dcc5f0b875bca325313debb9397b98d899de1 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 28 Nov 2020 20:13:45 +0800 Subject: [PATCH 113/129] remove the stupidity of the double-layer defn map of filename --- source/include/typecheck.h | 11 +- source/typecheck/dotop.cpp | 2 + source/typecheck/toplevel.cpp | 196 +++------------------------- source/typecheck/typecheckstate.cpp | 35 ++--- 4 files changed, 31 insertions(+), 213 deletions(-) diff --git a/source/include/typecheck.h b/source/include/typecheck.h index a0fc5601..48d32083 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -72,16 +72,7 @@ namespace sst util::hash_map> unresolvedGenericDefs; util::hash_map>, sst::Defn*> resolvedGenericDefs; - struct DefnMap - { - bool wasPublicImport = false; - util::hash_map> defns; - }; - - // maps from filename to defnmap -- allows tracking definitions by where they came from - // so we can resolve the import duplication bullshit - util::hash_map definitions; - + util::hash_map> definitions2; std::vector exports; std::vector imports; diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 65bb9a0e..2903ad34 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -847,6 +847,8 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, iceAssert(def); + // anti-TreeDefn gang: delete this entire if branch; it looks like a duplicate of + // the one for ScopeExpr below. if(auto td = dcast(sst::TreeDefn, def)) { auto newscope = td->tree->getScope(); diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 9fe028ca..4cbe246d 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -19,197 +19,39 @@ using namespace ast; namespace sst { - static StateTree* cloneTree(StateTree* clonee, StateTree* surrogateParent, const std::string& filename) + struct OsStrings { - auto clone = util::pool(clonee->name, filename, surrogateParent); - clone->treeDefn = util::pool(Location()); - clone->treeDefn->tree = clone; - - for(auto sub : clonee->subtrees) - clone->subtrees[sub.first] = cloneTree(sub.second, clone, filename); - - clone->definitions = clonee->definitions; - clone->unresolvedGenericDefs = clonee->unresolvedGenericDefs; - - - clone->infixOperatorOverloads = clonee->infixOperatorOverloads; - clone->prefixOperatorOverloads = clonee->prefixOperatorOverloads; - clone->postfixOperatorOverloads = clonee->postfixOperatorOverloads; + std::string name; + std::string vendor; + }; + static OsStrings getOsStrings(); + static void generatePreludeDefinitions(TypecheckState* fs); - return clone; + static void mergeTrees(StateTree* base, StateTree* branch) + { } - static StateTree* _addTreeToExistingTree(const std::unordered_set& thingsImported, StateTree* existing, - StateTree* tree, StateTree* commonParent, bool pubImport, bool ignoreVis, const std::string& importer) + static void checkConflictingDefinitions(sst::StateTree* base, sst::StateTree* branch) { - // if it was compiler-generated, just skip it. - if(tree->isCompilerGenerated) - return existing; - - // first merge all children -- copy whatever 1 has, plus what 1 and 2 have in common - for(auto sub : tree->subtrees) - { - if(sub.second->isCompilerGenerated) - continue; - - // debuglog("add subtree '%s' (%p) to tree '%s' (%p)\n", sub.first, sub.second, existing->name, existing); - if(auto it = existing->subtrees.find(sub.first); it != existing->subtrees.end()) - { - if(sub.second->treeDefn->visibility != VisibilityLevel::Public) - continue; - - _addTreeToExistingTree(thingsImported, existing->subtrees[sub.first], sub.second, existing, - pubImport, ignoreVis, importer); - } - else - { - existing->subtrees[sub.first] = cloneTree(sub.second, existing, tree->topLevelFilename); - } - } - - // then, add all functions and shit - for(auto& defs_with_src : tree->definitions) - { - auto filename = defs_with_src.first; - - // if we've already seen it, don't waste time re-importing it (and checking for dupes and stuff) - if(thingsImported.find(filename) != thingsImported.end()) - continue; - - for(auto defs : defs_with_src.second.defns) - { - auto name = defs.first; - for(auto def : defs.second) - { - // info(def, "hello there (%s)", def->visibility); - if(ignoreVis || ((pubImport || existing->topLevelFilename == importer) && def->visibility == VisibilityLevel::Public)) - { - auto others = existing->getDefinitionsWithName(name); - - for(auto ot : others) - { - if(auto fn = dcast(FunctionDecl, def)) - { - if(auto v = dcast(VarDefn, ot)) - { - SimpleError::make(fn->loc, "conflicting definition for function '%s'; was previously defined as a variable") - ->append(SimpleError::make(MsgType::Note, v->loc, "conflicting definition was here:")) - ->postAndQuit(); - } - else if(auto f = dcast(FunctionDecl, ot)) - { - if(fir::Type::areTypeListsEqual(zfu::map(fn->params, [](const auto& p) -> auto { return p.type; }), - zfu::map(f->params, [](const auto& p) -> fir::Type* { return p.type; }))) - { - debuglogln("from: %s", tree->topLevelFilename); - debuglogln("to: %s", existing->topLevelFilename); - SimpleError::make(fn->loc, "duplicate definition of function '%s' with identical signature", - fn->id.name) - ->append(SimpleError::make(MsgType::Note, f->loc, - "conflicting definition was here: (%p vs %p)", - reinterpret_cast(f), reinterpret_cast(fn))) - ->postAndQuit(); - } - } - else - { - error(def, "??"); - } - } - else if(auto vr = dcast(VarDefn, def)) - { - auto err = SimpleError::make(vr->loc, "duplicate definition for variable '%s'", name); - - for(auto ot : others) - err->append(SimpleError::make(MsgType::Note, ot->loc, "previously defined here:")); - - err->postAndQuit(); - } - else if(auto uvd = dcast(UnionVariantDefn, def)) - { - // these just... don't conflict. - if(auto ovd = dcast(UnionVariantDefn, ot); ovd) - { - if(ovd->parentUnion->original != uvd->parentUnion->original) - goto conflict; - } - else - { - // ! GOTO ! - goto conflict; - } - } - else - { - // probably a class or something - conflict: - SimpleError::make(def->loc, "duplicate definition of %s '%s'", def->readableName, def->id.name) - ->append(SimpleError::make(MsgType::Note, ot->loc, "conflicting definition was here:")) - ->postAndQuit(); - } - } - - warn(def, "add to %s", existing->topLevelFilename); - existing->addDefinition(tree->topLevelFilename, name, def); - } - else - { - warn(def, "skipping def %s because it is not public", def->id.name); - } - } - } - } + // for(const auto& [ _, defns ] : base->definitions) + // { + // for(const auto& [ name, def ] : defns.defns) + // { + // } + // } + } - for(auto f : tree->unresolvedGenericDefs) - { - // do a proper thing! - auto& ex = existing->unresolvedGenericDefs[f.first]; - for(auto x : f.second) - { - if(x->visibility == VisibilityLevel::Public && std::find(ex.begin(), ex.end(), x) == ex.end()) - ex.push_back(x); - } - } + void mergeExternalTree(sst::StateTree* base, sst::StateTree* branch) + { - for(auto& pair : { - std::make_pair(&existing->infixOperatorOverloads, tree->infixOperatorOverloads), - std::make_pair(&existing->prefixOperatorOverloads, tree->prefixOperatorOverloads), - std::make_pair(&existing->postfixOperatorOverloads, tree->postfixOperatorOverloads) - }) - { - auto& exst = *pair.first; - auto& add = pair.second; + } - for(const auto& f : add) - { - // do a proper thing! - auto& ex = exst[f.first]; - for(auto x : f.second) - { - if(x->visibility == VisibilityLevel::Public && std::find(ex.begin(), ex.end(), x) == ex.end()) - ex.push_back(x); - } - } - } - return existing; - } - StateTree* addTreeToExistingTree(StateTree* existing, StateTree* tree, StateTree* commonParent, bool pubImport, bool ignoreVis) - { - return _addTreeToExistingTree({ }, existing, tree, commonParent, pubImport, ignoreVis, existing->topLevelFilename); - } - struct OsStrings - { - std::string name; - std::string vendor; - }; - static OsStrings getOsStrings(); - static void generatePreludeDefinitions(TypecheckState* fs); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 68d56b30..e147b944 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -293,25 +293,13 @@ namespace sst util::hash_map> StateTree::getAllDefinitions() { - util::hash_map> ret; - for(auto srcs : this->definitions) - ret.insert(srcs.second.defns.begin(), srcs.second.defns.end()); - - return ret; + return this->definitions2; } static void fetchDefinitionsFrom(const std::string& name, StateTree* tree, bool recursively, std::vector& out) { - for(const auto& [ filename, defnMap ] : tree->definitions) - { - (void) filename; - if(auto it = defnMap.defns.find(name); it != defnMap.defns.end()) - { - const auto& defs = it->second; - if(defs.size() > 0) - out.insert(out.end(), defs.begin(), defs.end()); - } - } + if(auto it = tree->definitions2.find(name); it != tree->definitions2.end()) + out.insert(out.end(), it->second.begin(), it->second.end()); if(!recursively) return; @@ -345,7 +333,8 @@ namespace sst void StateTree::addDefinition(const std::string& sourceFile, const std::string& name, Defn* def, const TypeParamMap_t& gmaps) { // this->definitions[sourceFile][util::typeParamMapToString(name, gmaps)].push_back(def); - this->definitions[sourceFile].defns[name].push_back(def); + // this->definitions[sourceFile].defns[name].push_back(def); + this->definitions2[name].push_back(def); } void StateTree::addDefinition(const std::string& _name, Defn* def, const TypeParamMap_t& gmaps) @@ -620,17 +609,11 @@ namespace sst void StateTree::dump() { zpr::println("%*s* TREE: %s - %s", indent * 2, "", this->name, frontend::getFilenameFromPath(this->topLevelFilename)); - for(const auto& [ filename, dm ] : this->definitions) + for(const auto& [ name, defs ] : this->definitions2) { - zpr::println("%*s* FROM %s (%s)", (indent + 1) * 2, "", frontend::getFilenameFromPath(filename), - dm.wasPublicImport ? "public" : "private"); - - for(const auto& [ name, defs ] : dm.defns) - { - zpr::println("%*s* %s", (indent + 2) * 2, "", name); - for(auto d : defs) - zpr::println("%*s> %s: %s", (indent + 3) * 2, "", d->id.str(), d->type ? d->type->str() : "??"); - } + zpr::println("%*s* %s", (indent + 1) * 2, "", name); + for(auto d : defs) + zpr::println("%*s> %s: %s", (indent + 2) * 2, "", d->id.str(), d->type ? d->type->str() : "??"); } if(!this->subtrees.empty()) From 9808060e5a8a32501560cb24e7680c8d631e69fb Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sat, 28 Nov 2020 23:16:04 +0800 Subject: [PATCH 114/129] mark out the treedefn spots to remove --- build/tmp/repro_1.flx | 11 +++++++- makefile | 2 +- source/codegen/misc.cpp | 2 +- source/include/sst.h | 5 ++-- source/include/stcommon.h | 1 + source/platform/platform.cpp | 26 +++++++++-------- source/typecheck/dotop.cpp | 23 ++++++--------- source/typecheck/toplevel.cpp | 44 +++++++++++++++++++++++------ source/typecheck/typecheckstate.cpp | 8 ++++-- source/typecheck/using.cpp | 18 ++++-------- source/typecheck/variable.cpp | 28 ++++++++++++++---- 11 files changed, 106 insertions(+), 62 deletions(-) diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index a03a49a0..afa55772 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -7,6 +7,13 @@ import repro_2 using repro_2 as r +namespace omega +{ + let foo: int = 30 +} + +let omega: str = "owo" + @entry fn main() { // class Foozle @@ -25,7 +32,9 @@ using repro_2 as r // }; // } - printf("four = %d\n", r::Foo::FOUR.value); + // printf("four = %d\n", r::Foo::FOUR.value); + + printf("hello\n"); // using Foozle::Flub as Flubzle // Flubzle::omega(); diff --git a/makefile b/makefile index 0a973048..37153a62 100644 --- a/makefile +++ b/makefile @@ -39,7 +39,7 @@ NUMFILES := $$(($(words $(CXXSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++17 -fvisibility=hidden -O0 -g -c -Wall -frtti -fno-omit-frame-pointer $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -fvisibility=hidden -O3 -g -c -Wall -frtti -fno-omit-frame-pointer $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) -fvisibility=hidden diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index 2837de09..5a96ce79 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -12,7 +12,7 @@ CGResult sst::TypeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) CGResult sst::ScopeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) { - error(this, "failed to resolve scope '%s'", zfu::join(this->scope, "::")); + error(this, "failed to resolve scope '%s'", this->scope2.string()); } CGResult sst::TreeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) diff --git a/source/include/sst.h b/source/include/sst.h index eca0c752..be4f9071 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -401,12 +401,13 @@ namespace sst struct ScopeExpr : Expr { - ScopeExpr(const Location& l, fir::Type* t) : Expr(l, t) { this->readableName = ""; } + ScopeExpr(const Location& l, fir::Type* t, const Scope& scope) : Expr(l, t), scope2(scope) + { this->readableName = ""; } + ~ScopeExpr() { } virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; - std::vector scope; Scope scope2; }; diff --git a/source/include/stcommon.h b/source/include/stcommon.h index d8b4ef2e..3284ad1b 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -18,6 +18,7 @@ namespace sst StateTree* stree = 0; const Scope* prev = 0; + std::string string() const; std::vector getStrings() const; const Scope& appending(const std::string& name) const; }; diff --git a/source/platform/platform.cpp b/source/platform/platform.cpp index d9739dd6..8950b43e 100644 --- a/source/platform/platform.cpp +++ b/source/platform/platform.cpp @@ -185,7 +185,7 @@ namespace platform } - static util::hash_map cachedFileContents; + static util::hash_map cachedFileContents; void cachePreExistingFile(const std::string& path, const std::string& contents) { @@ -215,12 +215,12 @@ namespace platform // explanation: if we have EXTRA_MMAP_FLAGS, then we're getting 2MB pages -- in which case we should probably only do it // if we have at least 4mb worth of file. // if not, then just 2 * pagesize. - #define MINIMUM_MMAP_THRESHOLD (static_cast(EXTRA_MMAP_FLAGS ? (2 * 2 * 1024 * 1024) : 2 * getpagesize())) + #define MINIMUM_MMAP_THRESHOLD (static_cast(((EXTRA_MMAP_FLAGS) != 0) ? (2 * 2 * 1024 * 1024) : 2 * getpagesize())) - char* contents = 0; + std::string_view contents; - // here's the thing -- we use USE_MMAP at *compile-time*, because on windows some of the constants we're going to use here aren't available at all - // if we include it, then it'll be parsed and everything and error out. So, we #ifdef it away. + // here's the thing -- we use USE_MMAP at *compile-time*, because on windows some of the constants we're going to use + // here aren't available at all if we include it, then it'll be parsed and everything and error out. So, we #ifdef it away. // Problem is, there's another scenario in which we won't want to use mmap -- when the file size is too small. so, that's why the stuff // below is structured the way it is. @@ -229,34 +229,36 @@ namespace platform if(fileLength >= MINIMUM_MMAP_THRESHOLD) { // ok, do an mmap - contents = static_cast(mmap(0, fileLength, PROT_READ, MAP_PRIVATE | EXTRA_MMAP_FLAGS, fd, 0)); - if(contents == reinterpret_cast(-1)) + const char* buf = static_cast(mmap(0, fileLength, PROT_READ, MAP_PRIVATE | EXTRA_MMAP_FLAGS, fd, 0)); + if(buf == reinterpret_cast(-1)) { perror("there was an error reading the file"); exit(-1); } + + contents = std::string_view(buf, fileLength); } } #endif - if(contents == 0) + if(contents.empty()) { // read normally //! MEMORY LEAK - contents = new char[fileLength + 1]; - size_t didRead = platform::readFile(fd, contents, fileLength); + auto buf = new char[fileLength + 1]; + size_t didRead = platform::readFile(fd, buf, fileLength); if(didRead != fileLength) { perror("there was an error reading the file"); error("expected %d bytes, but read only %d", fileLength, didRead); } - cachedFileContents[path] = std::string(contents, fileLength); + contents = std::string_view(buf, fileLength); } - iceAssert(contents); closeFile(fd); + cachedFileContents[path] = contents; return cachedFileContents[path]; } diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 2903ad34..d3958239 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -754,7 +754,7 @@ static sst::Expr* checkRhs2(sst::TypecheckState* fs, ast::DotOperator* dot, cons else { error(dot->right, "unexpected %s on right-side of dot-operator following static scope '%s' on the left", dot->right->readableName, - zfu::join(news.getStrings(), "::")); + news.string()); } iceAssert(ret); @@ -860,11 +860,8 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, if(auto vr = dcast(sst::VarRef, expr); vr && dcast(sst::TreeDefn, vr->def)) { newscope.push_back(vr->name); - auto ret = util::pool(dot->loc, fir::Type::getVoid()); - ret->scope = newscope; - ret->scope2 = td->tree->getScope2().appending(vr->name); - - return ret; + return util::pool(dot->loc, fir::Type::getVoid(), + td->tree->getScope2().appending(vr->name)); } else { @@ -980,19 +977,15 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } else if(auto scp = dcast(sst::ScopeExpr, left)) { - auto oldscope = fs->getCurrentScope(); - auto newscope = scp->scope; + auto oldscope = fs->getCurrentScope2(); + auto newscope = scp->scope2; - auto expr = checkRhs(fs, dot, oldscope, newscope, infer); + auto expr = checkRhs2(fs, dot, oldscope, newscope, infer); + // anti-treedefn gang: just remove this entire thing, return expr. if(auto vr = dcast(sst::VarRef, expr); vr && dcast(sst::TreeDefn, vr->def)) { - newscope.push_back(vr->name); - auto ret = util::pool(dot->loc, fir::Type::getVoid()); - ret->scope = newscope; - ret->scope2 = scp->scope2.appending(vr->name); - - return ret; + return util::pool(dot->loc, fir::Type::getVoid(), scp->scope2.appending(vr->name)); } else { diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 4cbe246d..1501bd6b 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -31,20 +31,47 @@ namespace sst { } - static void checkConflictingDefinitions(sst::StateTree* base, sst::StateTree* branch) + static bool definitionsConflict(const sst::Defn* a, const sst::Defn* b) { - // for(const auto& [ _, defns ] : base->definitions) - // { - // for(const auto& [ name, def ] : defns.defns) - // { + return false; + } - // } - // } + static void checkConflictingDefinitions(const sst::StateTree* base, const sst::StateTree* branch) + { + for(const auto& [ name, defns ] : base->definitions2) + { + if(auto it = branch->definitions2.find(name); it != branch->definitions2.end()) + { + for(auto d1 : defns) + { + for(auto d2 : it->second) + { + if(definitionsConflict(d1, d2)) + { + error(d1->loc, "conflicting definitions"); + } + } + } + } + } } void mergeExternalTree(sst::StateTree* base, sst::StateTree* branch) { + // first check conflicts for this level: + checkConflictingDefinitions(base, branch); + // no problem -- attach the trees + base->imports.push_back(branch); + + // check recursively + // TODO: optimise this by looping over the tree with less subtrees and doing hash lookup + // on the one with more subtrees. + for(const auto& [ name, sub ] : base->subtrees) + { + if(auto st = branch->subtrees[name]) + mergeExternalTree(sub, st); + } } @@ -59,8 +86,7 @@ namespace sst - using frontend::CollectorState; - DefinitionTree* typecheck(CollectorState* cs, const parser::ParsedFile& file, + DefinitionTree* typecheck(frontend::CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports, bool addPreludeDefinitions) { StateTree* tree = new StateTree(file.moduleName, file.name, 0); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index e147b944..c23dd562 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -8,6 +8,7 @@ #include "ir/type.h" #include "memorypool.h" +#include "zfu.h" #include @@ -348,7 +349,10 @@ namespace sst - + std::string Scope::string() const + { + return zfu::join(this->getStrings(), "::"); + } std::vector Scope::getStrings() const { @@ -421,7 +425,7 @@ namespace sst StateTree* StateTree::searchForName(const std::string& name) { - auto tree = this; + auto tree = this->parent; while(tree) { if(tree->name == name) diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index ddabc38c..b4710019 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -30,26 +30,17 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) sst::Scope scopes2; - std::vector scopes; auto vr = dcast(sst::VarRef, used); if(vr && dcast(sst::EnumDefn, vr->def)) { auto enrd = dcast(sst::EnumDefn, vr->def); - - scopes = enrd->id.scope; - scopes.push_back(enrd->id.name); - scopes2 = enrd->innerScope; } // uses the same 'vr' from the branch above else if(vr && dcast(sst::UnionDefn, vr->def)) { auto unn = dcast(sst::UnionDefn, vr->def); - - scopes = unn->id.scope; - scopes.push_back(unn->id.name); - scopes2 = unn->innerScope; } else @@ -59,14 +50,15 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) // this happens in cases like `foo::bar` if(auto se = dcast(sst::ScopeExpr, used)) - scopes = se->scope, scopes2 = se->scope2; + scopes2 = se->scope2; // and this happens in cases like `foo` else if(vr && (td = dcast(sst::TypeDefn, vr->def))) - scopes = { vr->name }, scopes2 = td->innerScope; + scopes2 = td->innerScope; + // anti-treedefn gang: yeet this branch else if(vr && (tr = dcast(sst::TreeDefn, vr->def))) - scopes = { vr->name }, scopes2 = sst::Scope(tr->tree); + scopes2 = sst::Scope(tr->tree); else error("unsupported LHS of using: '%s'", used->readableName); @@ -83,7 +75,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(auto existing = fs->getDefinitionsWithName(this->useAs); existing.size() > 0) { auto err = SimpleError::make(this->loc, "cannot use scope '%s' as '%s'; one or more conflicting definitions exist", - zfu::join(scopes, "::"), this->useAs); + scopes2.string(), this->useAs); err->append(SimpleError::make(MsgType::Note, existing[0]->loc, "first conflicting definition here:")); err->postAndQuit(); diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 1185e3c6..75b65a94 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -2,9 +2,11 @@ // Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. +#include "defs.h" #include "pts.h" #include "ast.h" #include "errors.h" +#include "sst.h" #include "typecheck.h" #include "resolver.h" @@ -63,7 +65,7 @@ static TCResult checkPotentialCandidate(sst::TypecheckState* fs, ast::Ident* ide - + // anti-treedefn gang: get rid of this. if(auto treedef = dcast(sst::TreeDefn, def)) { return getResult(ident, treedef, false); @@ -148,13 +150,18 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) defer(fs->popLoc()); if(this->name == "_") + { return TCResult( SimpleError::make(this->loc, "'_' is a discarding binding; it does not yield a value and cannot be referred to") ); + } + + auto makeScopeExpr = [this](const sst::Scope& scope) -> sst::ScopeExpr* { + return util::pool(this->loc, fir::Type::getVoid(), scope); + }; - // else if(this->name == "::" || this->name == "^") - // error(this, "invalid use of scope-path-specifier '%s' in a non-scope-path context", this->name); + // anti-treedefn gang: if we contain any of these we should return a ScopeExpr instead. if(this->name == "::") { // find the root. @@ -163,7 +170,9 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) t = t->parent; iceAssert(t->treeDefn); - return getResult(this, t->treeDefn); + return TCResult(makeScopeExpr(t->getScope2())); + + // return getResult(this, t->treeDefn); } else if(this->name == "^") { @@ -180,14 +189,18 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) t = t->parent; iceAssert(t->treeDefn); - return getResult(this, t->treeDefn); + return TCResult(makeScopeExpr(t->getScope2())); + // return getResult(this, t->treeDefn); } } - if(auto builtin = fir::Type::fromBuiltin(this->name)) return TCResult(util::pool(this->loc, builtin)); + // check if there is a subtree with this name. + if(auto sub = fs->stree->subtrees[this->name]; sub != nullptr) + return TCResult(makeScopeExpr(sub->getScope2())); + if(infer && infer->containsPlaceholders()) infer = 0; @@ -195,12 +208,15 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(this->name == "this" && fs->isInFunctionBody() && fs->getCurrentFunction()->parentTypeForMethod) return TCResult(util::pool(this->loc, fs->getCurrentFunction()->parentTypeForMethod)); + // hm. sst::StateTree* tree = fs->stree; while(tree) { std::vector vs = tree->getDefinitionsWithName(this->name); + // anti-treedefn gang: we need to check for subtrees as well here, and return ScopeExpr + // for matching ones. if(vs.size() > 1) { std::vector> ambigs; From 54e9625dd8dff38bd94591949e569443632d6c3b Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 29 Nov 2020 01:36:08 +0800 Subject: [PATCH 115/129] eliminate treedefn --- build/tmp/repro_1.flx | 18 ++++----- makefile | 2 +- notes.md | 10 ----- source/codegen/misc.cpp | 5 --- source/include/sst.h | 13 ------- source/include/typecheck.h | 4 +- source/repl/execute.cpp | 3 -- source/typecheck/dotop.cpp | 37 ++---------------- source/typecheck/toplevel.cpp | 60 +++-------------------------- source/typecheck/typecheckstate.cpp | 15 +------- source/typecheck/using.cpp | 23 ++++------- source/typecheck/variable.cpp | 30 +++------------ 12 files changed, 33 insertions(+), 187 deletions(-) diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index afa55772..064fb6bf 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -2,17 +2,17 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. -import libc as _ -import repro_2 +import libc +// import repro_2 -using repro_2 as r +// using repro_2 as r -namespace omega -{ - let foo: int = 30 -} +// namespace omega +// { +// let foo: int = 30 +// } -let omega: str = "owo" +// let omega: str = "owo" @entry fn main() { @@ -34,7 +34,7 @@ let omega: str = "owo" // printf("four = %d\n", r::Foo::FOUR.value); - printf("hello\n"); + libc::printf("hello\n"); // using Foozle::Flub as Flubzle // Flubzle::omega(); diff --git a/makefile b/makefile index 37153a62..0a973048 100644 --- a/makefile +++ b/makefile @@ -39,7 +39,7 @@ NUMFILES := $$(($(words $(CXXSRC)))) DEFINES := -D__USE_MINGW_ANSI_STDIO=1 SANITISE := -CXXFLAGS += -std=c++17 -fvisibility=hidden -O3 -g -c -Wall -frtti -fno-omit-frame-pointer $(SANITISE) $(DEFINES) +CXXFLAGS += -std=c++17 -fvisibility=hidden -O0 -g -c -Wall -frtti -fno-omit-frame-pointer $(SANITISE) $(DEFINES) LDFLAGS += $(SANITISE) -fvisibility=hidden diff --git a/notes.md b/notes.md index 0046973c..446f9998 100644 --- a/notes.md +++ b/notes.md @@ -31,12 +31,6 @@ the imports list of the current scope. ## to refactor -1. we should probably get rid of TreeDefn - - a lot of places are "forced" to setup a TreeDefn when they setup a new StateTree scope - - from a cursory inspection, it's only used for "concretising" the interim parts of `a::b::c`, so we - can parse it as `(a::b)::c` and then perform resolution *as-if* it is a normal scope definition - - i'm sure there's a way to refactor this... hopefully. - 2. a lot of places probably still have the concept of `scope == std::vector`. - after the first scope refactor, i think these instances will be reduced - undoubtedly there will be more. for instance, Identifier holds the scope as exactly that. @@ -61,10 +55,6 @@ the imports list of the current scope. field; then, we should be able to simplify the typechecking code substantially by moving those the polymorph instantiation into that definition's typecheck method instead. -5. the definitions map should probably just get rid of extra level of mapping from file source to list, since i think - that it's only used for the scuffed addTreeToExistingTree thing. - - ## to investigate diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index 5a96ce79..37d359bf 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -15,11 +15,6 @@ CGResult sst::ScopeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) error(this, "failed to resolve scope '%s'", this->scope2.string()); } -CGResult sst::TreeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) -{ - error(this, "cannot codegen tree definition -- something fucked up somewhere"); -} - CGResult sst::BareTypeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) { // there's nothing to do here... diff --git a/source/include/sst.h b/source/include/sst.h index be4f9071..38ea5598 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -572,19 +572,6 @@ namespace sst }; - - - struct TreeDefn : Defn - { - TreeDefn(const Location& l) : Defn(l) { this->readableName = ""; } - ~TreeDefn() { } - - virtual std::string getKind() override { return "namespace"; } - virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; - - StateTree* tree = 0; - }; - struct NamespaceDefn : Stmt { NamespaceDefn(const Location& l) : Stmt(l) { this->readableName = "namespace"; } diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 48d32083..261b11f3 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -55,7 +55,6 @@ namespace sst std::string topLevelFilename; StateTree* parent = 0; - TreeDefn* treeDefn = 0; // for those anonymous scopes (with numbers) that we create. // currently we only keep track of this for scope-path resolution, so we can skip them @@ -237,8 +236,7 @@ namespace sst const std::vector>& imports, bool addPreludeDefinitions); - StateTree* addTreeToExistingTree(StateTree* to, StateTree* from, StateTree* commonParent, bool pubImport, bool ignoreVis); - + void mergeExternalTree(sst::StateTree* base, sst::StateTree* branch); std::vector collateGenericArgStacks(); } diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index eda9d638..8a3f2c1c 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -31,9 +31,6 @@ namespace repl this->module = new fir::Module(modname); sst::StateTree* tree = new sst::StateTree(modname, modname, 0); - tree->treeDefn = util::pool(Location()); - tree->treeDefn->tree = tree; - this->fs = new sst::TypecheckState(tree); this->cs = new cgn::CodegenState(fir::IRBuilder(this->module)); this->cs->module = this->module; diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index d3958239..91033b8a 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -193,7 +193,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d auto type = lhs->type; if(!type) { - if(dcast(sst::ScopeExpr, lhs) || (dcast(sst::VarRef, lhs) && dcast(sst::TreeDefn, dcast(sst::VarRef, lhs)->def))) + if(dcast(sst::ScopeExpr, lhs)) { error(dotop, "invalid use of '.' for static scope access; use '::' instead"); } @@ -847,28 +847,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, iceAssert(def); - // anti-TreeDefn gang: delete this entire if branch; it looks like a duplicate of - // the one for ScopeExpr below. - if(auto td = dcast(sst::TreeDefn, def)) - { - auto newscope = td->tree->getScope(); - auto oldscope = fs->getCurrentScope(); - - auto expr = checkRhs(fs, dot, oldscope, newscope, infer); - - // check the thing - if(auto vr = dcast(sst::VarRef, expr); vr && dcast(sst::TreeDefn, vr->def)) - { - newscope.push_back(vr->name); - return util::pool(dot->loc, fir::Type::getVoid(), - td->tree->getScope2().appending(vr->name)); - } - else - { - return expr; - } - } - else if(auto typdef = dcast(sst::TypeDefn, def)) + if(auto typdef = dcast(sst::TypeDefn, def)) { if(dcast(sst::ClassDefn, def) || dcast(sst::StructDefn, def)) { @@ -980,17 +959,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, auto oldscope = fs->getCurrentScope2(); auto newscope = scp->scope2; - auto expr = checkRhs2(fs, dot, oldscope, newscope, infer); - - // anti-treedefn gang: just remove this entire thing, return expr. - if(auto vr = dcast(sst::VarRef, expr); vr && dcast(sst::TreeDefn, vr->def)) - { - return util::pool(dot->loc, fir::Type::getVoid(), scp->scope2.appending(vr->name)); - } - else - { - return expr; - } + return checkRhs2(fs, dot, oldscope, newscope, infer); } error("????!!!!"); diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 1501bd6b..9f56ba19 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -27,10 +27,6 @@ namespace sst static OsStrings getOsStrings(); static void generatePreludeDefinitions(TypecheckState* fs); - static void mergeTrees(StateTree* base, StateTree* branch) - { - } - static bool definitionsConflict(const sst::Defn* a, const sst::Defn* b) { return false; @@ -69,8 +65,8 @@ namespace sst // on the one with more subtrees. for(const auto& [ name, sub ] : base->subtrees) { - if(auto st = branch->subtrees[name]) - mergeExternalTree(sub, st); + if(auto it = branch->subtrees.find(name); it != branch->subtrees.end()) + mergeExternalTree(sub, it->second); } } @@ -89,11 +85,7 @@ namespace sst DefinitionTree* typecheck(frontend::CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports, bool addPreludeDefinitions) { - StateTree* tree = new StateTree(file.moduleName, file.name, 0); - tree->treeDefn = util::pool(Location()); - tree->treeDefn->enclosingScope = Scope(); - tree->treeDefn->tree = tree; - + auto tree = new StateTree(file.moduleName, file.name, 0); auto fs = new TypecheckState(tree); for(auto [ ithing, import ] : imports) @@ -133,20 +125,6 @@ namespace sst auto newinspt = util::pool(impas, file.name, curinspt); curinspt->subtrees[impas] = newinspt; - auto treedef = util::pool(cs->dtrees[ithing.name]->topLevel->loc); - treedef->id = Identifier(impas, IdKind::Name); - treedef->tree = newinspt; - - // this is the parent scope! - treedef->enclosingScope = Scope(curinspt); - - treedef->tree->treeDefn = treedef; - treedef->visibility = ithing.pubImport - ? VisibilityLevel::Public - : VisibilityLevel::Private; - - curinspt->addDefinition(file.name, impas, treedef); - curinspt = newinspt; } } @@ -156,19 +134,8 @@ namespace sst iceAssert(insertPoint); - insertPoint->imports.push_back(import->base); - { - // make a new treedef referring to the newly-imported tree. - auto treedef = util::pool(ithing.loc); - treedef->tree = import->base; - treedef->enclosingScope = sst::Scope(insertPoint); - - insertPoint->addDefinition(import->base->name, treedef); - - - zpr::println("import: using tree %p, defn = %p", import->base, treedef); - } - + // insertPoint->imports.push_back(import->base); + mergeExternalTree(insertPoint, import->base); if(ithing.pubImport) insertPoint->reexports.push_back(import->base); @@ -270,9 +237,6 @@ namespace sst defer(fs->popTree()); - // manually add the definition, because we didn't typecheck a namespace or anything. - fs->stree->parent->addDefinition(fs->stree->name, fs->stree->treeDefn); - auto strty = fir::Type::getCharSlice(false); { @@ -380,20 +344,6 @@ TCResult ast::TopLevelBlock::typecheck(sst::TypecheckState* fs, fir::Type* infer } } - if(tree->parent) - { - auto td = tree->treeDefn; - iceAssert(td); - - td->visibility = this->visibility; - - if(auto err = fs->checkForShadowingOrConflictingDefinition(td, [](auto, auto) -> bool { return true; }, tree->parent)) - return TCResult(err); - - tree->parent->addDefinition(tree->topLevelFilename, td->id.name, td); - } - - if(this->name != "") fs->popTree(); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index c23dd562..2b83b79d 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -374,7 +374,6 @@ namespace sst { this->cachedScope.stree = this; - // all hail recursion. if(this->parent) this->cachedScope.prev = &this->parent->getScope2(); } @@ -447,15 +446,6 @@ namespace sst { auto newtree = util::pool(name, this->topLevelFilename, this, anonymous); this->subtrees[name] = newtree; - - // make a treedef. - newtree->treeDefn = util::pool(Location()); - - // this is the parent! - newtree->treeDefn->enclosingScope = Scope(this); - - newtree->treeDefn->tree = newtree; - return newtree; } } @@ -539,7 +529,7 @@ namespace sst for(auto otherdef : defs) { - if(!dcast(TreeDefn, otherdef) && !otherdef->type->containsPlaceholders() && conflictCheckCallback(this, otherdef)) + if(!otherdef->type->containsPlaceholders() && conflictCheckCallback(this, otherdef)) { auto errs = makeTheError(defn, defn->id.name, defn->getKind(), { std::make_pair(otherdef, otherdef->getKind()) }); @@ -637,12 +627,9 @@ namespace sst } } - - Scope::Scope(StateTree* st) { this->prev = 0; - this->stree = st; } } diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index b4710019..579aa217 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -29,36 +29,31 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) // and only getting ScopeExpr if it's really a namespace reference. - sst::Scope scopes2; + sst::Scope scopes; auto vr = dcast(sst::VarRef, used); if(vr && dcast(sst::EnumDefn, vr->def)) { auto enrd = dcast(sst::EnumDefn, vr->def); - scopes2 = enrd->innerScope; + scopes = enrd->innerScope; } // uses the same 'vr' from the branch above else if(vr && dcast(sst::UnionDefn, vr->def)) { auto unn = dcast(sst::UnionDefn, vr->def); - scopes2 = unn->innerScope; + scopes = unn->innerScope; } else { sst::TypeDefn* td = nullptr; - sst::TreeDefn* tr = nullptr; // this happens in cases like `foo::bar` if(auto se = dcast(sst::ScopeExpr, used)) - scopes2 = se->scope2; + scopes = se->scope2; // and this happens in cases like `foo` else if(vr && (td = dcast(sst::TypeDefn, vr->def))) - scopes2 = td->innerScope; - - // anti-treedefn gang: yeet this branch - else if(vr && (tr = dcast(sst::TreeDefn, vr->def))) - scopes2 = sst::Scope(tr->tree); + scopes = td->innerScope; else error("unsupported LHS of using: '%s'", used->readableName); @@ -67,7 +62,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(this->useAs == "_") { // TODO: check scope merge conflicts - fs->stree->imports.push_back(scopes2.stree); + sst::mergeExternalTree(fs->stree, scopes.stree); } else { @@ -75,16 +70,14 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(auto existing = fs->getDefinitionsWithName(this->useAs); existing.size() > 0) { auto err = SimpleError::make(this->loc, "cannot use scope '%s' as '%s'; one or more conflicting definitions exist", - scopes2.string(), this->useAs); + scopes.string(), this->useAs); err->append(SimpleError::make(MsgType::Note, existing[0]->loc, "first conflicting definition here:")); err->postAndQuit(); } auto tree = fs->stree->findOrCreateSubtree(this->useAs); - - tree->imports.push_back(scopes2.stree); - fs->stree->addDefinition(this->useAs, tree->treeDefn); + sst::mergeExternalTree(tree, scopes.stree); } return TCResult::getDummy(); diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 75b65a94..ad97ee02 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -64,13 +64,7 @@ static TCResult checkPotentialCandidate(sst::TypecheckState* fs, ast::Ident* ide } - - // anti-treedefn gang: get rid of this. - if(auto treedef = dcast(sst::TreeDefn, def)) - { - return getResult(ident, treedef, false); - } - else if(def->type->isUnionVariantType()) + if(def->type->isUnionVariantType()) { auto uvd = dcast(sst::UnionVariantDefn, def); iceAssert(uvd); @@ -160,8 +154,6 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) return util::pool(this->loc, fir::Type::getVoid(), scope); }; - - // anti-treedefn gang: if we contain any of these we should return a ScopeExpr instead. if(this->name == "::") { // find the root. @@ -169,10 +161,7 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) while(t->parent) t = t->parent; - iceAssert(t->treeDefn); return TCResult(makeScopeExpr(t->getScope2())); - - // return getResult(this, t->treeDefn); } else if(this->name == "^") { @@ -188,36 +177,28 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) while(t->isAnonymous && t->parent) t = t->parent; - iceAssert(t->treeDefn); return TCResult(makeScopeExpr(t->getScope2())); - // return getResult(this, t->treeDefn); } } if(auto builtin = fir::Type::fromBuiltin(this->name)) return TCResult(util::pool(this->loc, builtin)); - // check if there is a subtree with this name. - if(auto sub = fs->stree->subtrees[this->name]; sub != nullptr) - return TCResult(makeScopeExpr(sub->getScope2())); - - if(infer && infer->containsPlaceholders()) infer = 0; if(this->name == "this" && fs->isInFunctionBody() && fs->getCurrentFunction()->parentTypeForMethod) return TCResult(util::pool(this->loc, fs->getCurrentFunction()->parentTypeForMethod)); - // hm. sst::StateTree* tree = fs->stree; while(tree) { - std::vector vs = tree->getDefinitionsWithName(this->name); + // check if there is a subtree with this name. + if(auto it = tree->subtrees.find(this->name); it != tree->subtrees.end()) + return TCResult(makeScopeExpr(it->second->getScope2())); - // anti-treedefn gang: we need to check for subtrees as well here, and return ScopeExpr - // for matching ones. - if(vs.size() > 1) + if(auto vs = tree->getDefinitionsWithName(this->name); vs.size() > 1) { std::vector> ambigs; @@ -305,7 +286,6 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) } // ok, we haven't found anything - fs->stree->dump(); return TCResult( SimpleError::make(this->loc, "reference to unknown entity '%s'", this->name) ); From ef776ff38667b2ab46be358a658d95f5eb0fef1a Mon Sep 17 00:00:00 2001 From: zhiayang Date: Sun, 29 Nov 2020 10:42:46 +0800 Subject: [PATCH 116/129] finish refactor, time to fix regressions --- build/tests/intlimits.flx | 18 ++-- build/tmp/repro_1.flx | 38 ++++----- notes.md | 3 + source/fir/interp/interpreter.cpp | 2 +- source/include/ast.h | 3 - source/include/defs.h | 23 +++++- source/include/stcommon.h | 13 --- source/include/typecheck.h | 11 --- source/misc/identifier.cpp | 6 +- source/repl/execute.cpp | 3 - source/typecheck/classes.cpp | 27 +----- source/typecheck/dotop.cpp | 85 ++----------------- source/typecheck/enums.cpp | 5 +- source/typecheck/function.cpp | 18 ++-- source/typecheck/misc.cpp | 4 +- source/typecheck/structs.cpp | 31 +++---- source/typecheck/toplevel.cpp | 10 +-- source/typecheck/traits.cpp | 20 ++--- source/typecheck/typecheckstate.cpp | 123 ++-------------------------- source/typecheck/unions.cpp | 21 +++-- source/typecheck/using.cpp | 1 - source/typecheck/variable.cpp | 21 ++--- 22 files changed, 132 insertions(+), 354 deletions(-) diff --git a/build/tests/intlimits.flx b/build/tests/intlimits.flx index d101b4d4..5f5a9be5 100644 --- a/build/tests/intlimits.flx +++ b/build/tests/intlimits.flx @@ -9,15 +9,15 @@ import std::limits public fn printIntegerLimits() { - libc::printf(" i8::min = %hhd\t\t\t i8::max = %hhd\n", std::limits::int8::min, std::limits::int8::max); - libc::printf("i16::min = %hd\t\t\ti16::max = %hd\n", std::limits::int16::min, std::limits::int16::max); - libc::printf("i32::min = %d\t\t\ti32::max = %d\n", std::limits::int32::min, std::limits::int32::max); - libc::printf("i64::min = %lld\ti64::max = %lld\n", std::limits::int64::min, std::limits::int64::max); + // libc::printf(" i8::min = %hhd\t\t\t i8::max = %hhd\n", std::limits::int8::min, std::limits::int8::max); + // libc::printf("i16::min = %hd\t\t\ti16::max = %hd\n", std::limits::int16::min, std::limits::int16::max); + // libc::printf("i32::min = %d\t\t\ti32::max = %d\n", std::limits::int32::min, std::limits::int32::max); + // libc::printf("i64::min = %lld\ti64::max = %lld\n", std::limits::int64::min, std::limits::int64::max); - libc::printf("\n") + // libc::printf("\n") - libc::printf(" u8::min = %hhu\t\t\t\t u8::max = %hhu\n", std::limits::uint8::min,std::limits:: uint8::max); - libc::printf("u16::min = %hu\t\t\t\tu16::max = %hu\n", std::limits::uint16::min, std::limits::uint16::max); - libc::printf("u32::min = %u\t\t\t\tu32::max = %u\n", std::limits::uint32::min, std::limits::uint32::max); - libc::printf("u64::min = %llu\t\t\t\tu64::max = %llu\n", std::limits::uint64::min, std::limits::uint64::max); + // libc::printf(" u8::min = %hhu\t\t\t\t u8::max = %hhu\n", std::limits::uint8::min,std::limits:: uint8::max); + // libc::printf("u16::min = %hu\t\t\t\tu16::max = %hu\n", std::limits::uint16::min, std::limits::uint16::max); + // libc::printf("u32::min = %u\t\t\t\tu32::max = %u\n", std::limits::uint32::min, std::limits::uint32::max); + // libc::printf("u64::min = %llu\t\t\t\tu64::max = %llu\n", std::limits::uint64::min, std::limits::uint64::max); } diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index 064fb6bf..8c0bc5f3 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -2,8 +2,8 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. -import libc -// import repro_2 +import libc as _ +import repro_2 as _ // using repro_2 as r @@ -16,31 +16,31 @@ import libc @entry fn main() { - // class Foozle - // { - // init() { } + class Foozle + { + init() { } - // static fn alpha() { } + static fn alpha() { } - // class Flub - // { - // init() { } + class Flub + { + init() { } - // static fn beta() { } + static fn beta() { } - // static fn omega() { printf("owo\n") } - // }; - // } + static fn omega() { printf("owo\n") } + }; + } - // printf("four = %d\n", r::Foo::FOUR.value); + printf("four = %d\n", Foo::FOUR.value); - libc::printf("hello\n"); + printf("hello\n"); - // using Foozle::Flub as Flubzle - // Flubzle::omega(); + using Foozle::Flub as Flubzle + Flubzle::omega(); - // printf("%d\n", Bar::bla(3)); - // printf("lmao\n"); + printf("%d\n", Bar::bla(3)); + printf("lmao\n"); // let c = "a" + "b" } diff --git a/notes.md b/notes.md index 446f9998..91bd401f 100644 --- a/notes.md +++ b/notes.md @@ -55,6 +55,9 @@ the imports list of the current scope. field; then, we should be able to simplify the typechecking code substantially by moving those the polymorph instantiation into that definition's typecheck method instead. +5. setting the method's containing class via the `infer` parameter feels super fucking dirty, and ngl it has felt dirty + for the past 5 years. + ## to investigate diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 319ba54c..4cd0ca89 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -362,7 +362,7 @@ namespace interp is->globalAllocs.push_back(buffer); uint8_t* ofs = reinterpret_cast(buffer); - for(const auto& x : theArray->getValues()) + for(auto x : theArray->getValues()) { auto v = makeConstant(is, x); diff --git a/source/include/ast.h b/source/include/ast.h index d32a2a33..15a79117 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -93,7 +93,6 @@ namespace ast // another hack-y thing TypeDefn* parentType = 0; - VisibilityLevel visibility = VisibilityLevel::Internal; // hacky thing #3 @@ -103,8 +102,6 @@ namespace ast //? we set this in typecheck/toplevel.cpp when generating the declarations. //? for methods & nested types, we set them in structs.cpp/classes.cpp //? in repl mode, we set this manually. - std::vector realScope; - sst::Scope enclosingScope; }; diff --git a/source/include/defs.h b/source/include/defs.h index ab967c62..1e343d0e 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -141,13 +141,34 @@ template std::string strbold(const char* fmt, Ts&&... ts) return std::string(COLOUR_RESET) + std::string(COLOUR_BLACK_BOLD) + strprintf(fmt, ts...) + std::string(COLOUR_RESET); } +namespace sst +{ + struct StateTree; + + struct Scope + { + Scope() { } + Scope(StateTree* st); + + StateTree* stree = 0; + const Scope* prev = 0; + + mutable std::vector cachedComponents; + + std::string string() const; + const std::vector& components() const; + const Scope& appending(const std::string& name) const; + }; +} + struct Identifier { Identifier() : name(""), kind(IdKind::Invalid) { } Identifier(const std::string& n, IdKind k) : name(n), kind(k) { } std::string name; - std::vector scope; + sst::Scope scope2; + std::vector scope_; std::vector params; IdKind kind; diff --git a/source/include/stcommon.h b/source/include/stcommon.h index 3284ad1b..0e10e005 100644 --- a/source/include/stcommon.h +++ b/source/include/stcommon.h @@ -9,19 +9,6 @@ namespace sst { struct VarDefn; struct StateTree; - - struct Scope - { - Scope() { } - Scope(StateTree* st); - - StateTree* stree = 0; - const Scope* prev = 0; - - std::string string() const; - std::vector getStrings() const; - const Scope& appending(const std::string& name) const; - }; } namespace ast diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 261b11f3..72e1b209 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -88,7 +88,6 @@ namespace sst Scope cachedScope; const Scope& getScope2(); - std::vector getScope(); StateTree* searchForName(const std::string& name); StateTree* findOrCreateSubtree(const std::string& name, bool anonymous = false); @@ -189,17 +188,7 @@ namespace sst std::string serialiseCurrentScope(); - [[deprecated]] - std::vector getCurrentScope(); - - [[deprecated]] - void teleportToScope(const std::vector& scope); - - [[deprecated]] - StateTree* getTreeOfScope(const std::vector& scope); - Scope getCurrentScope2(); - // StateTree* getTree std::vector teleportationStack; void teleportInto(const Scope& scope); diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 4c9620b3..442730c2 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -118,7 +118,7 @@ bool Identifier::operator != (const Identifier& other) const std::string Identifier::str() const { std::string ret; - for(const auto& s : this->scope) + for(const auto& s : this->scope2.components()) ret += s + "."; ret += this->name; @@ -144,7 +144,7 @@ static std::string mangleScopeOnly(const Identifier& id) { bool first = true; std::string ret; - for(const auto& s : id.scope) + for(const auto& s : id.scope2.components()) { ret += (!first ? std::to_string(s.length()) : "") + s; first = false; @@ -252,7 +252,7 @@ static std::string _doMangle(const Identifier& id, bool includeScope) if(includeScope) scp += mangleScopeOnly(id); - if(includeScope && id.scope.size() > 0) + if(includeScope && id.scope2.prev != nullptr) scp += std::to_string(id.name.length()); return scp + id.name; diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index 8a3f2c1c..a3c2302c 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -121,10 +121,7 @@ namespace repl // note: usually, visitDeclarables in the top-level typecheck will set the realScope. // BUT, since we're not doing that, we must set it manually! if(auto def = dcast(ast::Parameterisable, stmt); def) - { - def->realScope = state->fs->getCurrentScope(); def->enclosingScope = state->fs->getCurrentScope2(); - } tcr = stmt->typecheck(state->fs); } diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index 84170ec2..3634b0c0 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -3,6 +3,7 @@ // Licensed under the Apache License Version 2.0. #include "ast.h" +#include "defs.h" #include "pts.h" #include "errors.h" @@ -36,7 +37,7 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope = this->realScope; + defn->id.scope2 = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; @@ -47,20 +48,17 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* for(auto m : this->methods) { m->parentType = this; - m->realScope = this->realScope + defn->id.name; m->enclosingScope = defn->innerScope; } for(auto m : this->initialisers) { m->parentType = this; - m->realScope = this->realScope + defn->id.name; m->enclosingScope = defn->innerScope; } for(auto m : this->staticMethods) { - m->realScope = this->realScope + defn->id.name; m->enclosingScope = defn->innerScope; } @@ -108,29 +106,20 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* // add it first so we can use it in the method bodies, // and make pointers to it { - // fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); defn->enclosingScope.stree->addDefinition(defnname, defn, gmaps); fs->typeDefnMap[cls] = defn; } - // auto oldscope = fs->getCurrentScope(); - // fs->teleportToScope(defn->id.scope); - // fs->pushTree(defn->id.name); fs->teleportInto(defn->innerScope); { for(auto t : this->nestedTypes) { - t->realScope = this->realScope + defn->id.name; t->enclosingScope = defn->innerScope; t->generateDeclaration(fs, 0, { }); } } fs->teleportOut(); - // fs->popTree(); - // fs->teleportToScope(oldscope); - - this->genericVersions.push_back({ defn, fs->getGenericContextStack() }); return TCResult(defn); } @@ -153,10 +142,6 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co auto cls = defn->type->toClassType(); iceAssert(cls); - // auto oldscope = fs->getCurrentScope(); - // fs->teleportToScope(defn->id.scope); - // fs->pushTree(defn->id.name); - fs->teleportInto(defn->innerScope); if(this->initialisers.empty()) @@ -364,8 +349,8 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co // basically, the only things we want to import from the base class are fields and methods -- not initialisers. // base-class-constructors must be called using `super(...)` syntax. - auto scp = defn->baseClass->id.scope + defn->baseClass->id.name; - auto tree = fs->getTreeOfScope(scp); + auto tree = defn->baseClass->innerScope.stree; + iceAssert(tree); std::function recursivelyImport = [&](sst::StateTree* from, sst::StateTree* to) -> void { @@ -436,9 +421,6 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co fs->teleportOut(); - // fs->popTree(); - // fs->teleportToScope(oldscope); - this->finishedTypechecking.insert(defn); return TCResult(defn); } @@ -533,7 +515,6 @@ TCResult ast::InitFunctionDefn::generateDeclaration(sst::TypecheckState* fs, fir this->actualDefn->parentType = this->parentType; this->actualDefn->returnType = pts::NamedType::create(this->loc, VOID_TYPE_STRING); - this->actualDefn->realScope = this->realScope; this->actualDefn->enclosingScope = this->enclosingScope; //* note: constructors will always mutate, definitely. diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 91033b8a..56534994 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -501,9 +501,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d arguments.insert(arguments.begin(), FnCallArgument::make(fc->loc, "this", str->type->getMutablePointerTo(), /* ignoreName: */ true)); - auto restore = fs->getCurrentScope(); - fs->teleportToScope(str->id.scope + str->id.name); - defer(fs->teleportToScope(restore)); + fs->teleportInto(str->innerScope); ErrorMsg* err = 0; sst::Defn* resolved = 0; @@ -523,7 +521,10 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(auto cls = dcast(sst::ClassDefn, curstr); cls && cls->baseClass) { - fs->teleportToScope(cls->baseClass->id.scope + cls->baseClass->id.name); + // teleport out, then back in. + fs->teleportOut(); + fs->teleportInto(cls->baseClass->innerScope); + curstr = cls->baseClass; continue; } @@ -577,6 +578,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d ret->lhs = lhs; ret->call = finalCall; + fs->teleportOut(); return ret; } else if(auto fld = dcast(ast::Ident, dotop->right)) @@ -759,7 +761,6 @@ static sst::Expr* checkRhs2(sst::TypecheckState* fs, ast::DotOperator* dot, cons iceAssert(ret); - // fs->teleportToScope(olds); fs->teleportOut(); return ret; } @@ -769,66 +770,6 @@ static sst::Expr* checkRhs2(sst::TypecheckState* fs, ast::DotOperator* dot, cons static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, sst::Expr* left, fir::Type* infer) { - auto checkRhs = [](sst::TypecheckState* fs, ast::DotOperator* dot, const std::vector& olds, const std::vector& news, - fir::Type* rhs_infer, sst::StructDefn* possibleStructDefn = 0) -> sst::Expr* { - - if(auto id = dcast(ast::Ident, dot->right)) - id->traverseUpwards = false; - - else if(auto fc = dcast(ast::FunctionCall, dot->right)) - fc->traverseUpwards = false; - - // note: for function/expr calls, we typecheck the arguments *before* we teleport to the scope, so that we don't conflate - // the scope of the argument (which is the current scope) with the scope of the call target (which is in whatever namespace) - - sst::Expr* ret = 0; - if(auto fc = dcast(ast::FunctionCall, dot->right)) - { - auto args = zfu::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, - e.second->typecheck(fs).expr(), e.second); - }); - - fs->teleportToScope(news); - ret = fc->typecheckWithArguments(fs, args, rhs_infer); - } - else if(auto ec = dcast(ast::ExprCall, dot->right)) - { - auto args = zfu::map(fc->args, [fs](auto e) -> FnCallArgument { return FnCallArgument(e.second->loc, e.first, - e.second->typecheck(fs).expr(), e.second); - }); - - fs->teleportToScope(news); - ret = ec->typecheckWithArguments(fs, args, rhs_infer); - } - else if(dcast(ast::Ident, dot->right) || dcast(ast::DotOperator, dot->right)) - { - fs->teleportToScope(news); - auto res = dot->right->typecheck(fs, rhs_infer); - if(res.isError() && possibleStructDefn && dcast(ast::Ident, dot->right)) - { - wrongDotOpError(res.error(), possibleStructDefn, dot->right->loc, dcast(ast::Ident, dot->right)->name, true)->postAndQuit(); - } - else - { - // will post if res is an error, even if we didn't give the fancy error. - ret = res.expr(); - } - } - else - { - error(dot->right, "unexpected %s on right-side of dot-operator following static scope '%s' on the left", dot->right->readableName, - zfu::join(news, "::")); - } - - - iceAssert(ret); - - fs->teleportToScope(olds); - return ret; - }; - - - // if we get a type expression, then we want to dig up the definition from the type. if(auto ident = dcast(sst::VarRef, left); ident || dcast(sst::TypeExpr, left)) { @@ -856,10 +797,6 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, auto oldscope = fs->getCurrentScope2(); auto newscope = typdef->innerScope; - // auto oldscope = fs->getCurrentScope(); - // auto newscope = typdef->id.scope; - // newscope.push_back(typdef->id.name); - fs->pushGenericContext(); defer(fs->popGenericContext()); { @@ -870,7 +807,6 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, fs->addGenericMapping(g.first, fir::PolyPlaceholderType::get(g.first, pses)); } - // auto ret = checkRhs(fs, dot, oldscope, newscope, infer); auto ret = checkRhs2(fs, dot, oldscope, newscope, infer); fs->popSelfContext(); @@ -919,18 +855,15 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } // dot-op on the union to access its variants; we need constructor stuff for it. - auto oldscope = fs->getCurrentScope(); - auto newscope = unn->id.scope; - newscope.push_back(unn->id.name); + auto oldscope = fs->getCurrentScope2(); + auto newscope = unn->innerScope; - return checkRhs(fs, dot, oldscope, newscope, infer); + return checkRhs2(fs, dot, oldscope, newscope, infer); } else if(auto enm = dcast(sst::EnumDefn, def)) { auto oldscope = fs->getCurrentScope2(); auto newscope = enm->innerScope; - // auto newscope = enm->id.scope; - // newscope.push_back(enm->id.name); auto rhs = checkRhs2(fs, dot, oldscope, newscope, infer); diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index c770dcce..9fe8c11b 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -40,7 +40,7 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->bareName = this->name; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope = this->realScope; + defn->id.scope2 = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; @@ -52,7 +52,6 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) return TCResult(err); - // fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); defn->enclosingScope.stree->addDefinition(defnname, defn, gmaps); this->genericVersions.push_back({ defn, fs->getGenericContextStack() }); @@ -96,7 +95,7 @@ TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con auto ecd = util::pool(cs.loc); ecd->id = Identifier(cs.name, IdKind::Name); - ecd->id.scope = fs->getCurrentScope(); + ecd->id.scope2 = fs->getCurrentScope2(); ecd->type = ety; ecd->parentEnum = defn; ecd->val = val; diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 188780c0..9d35f6a1 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -60,8 +60,9 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->bareName = this->name; defn->id = Identifier(this->name, IdKind::Function); - defn->id.scope = this->realScope; + defn->id.scope2 = this->enclosingScope; defn->id.params = ptys; + defn->enclosingScope = this->enclosingScope; defn->params = ps; defn->returnType = retty; @@ -106,10 +107,10 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* return TCResult(conflict_err); if(!defn->type->containsPlaceholders()) - fs->getTreeOfScope(this->realScope)->addDefinition(this->name, defn, gmaps); + defn->enclosingScope.stree->addDefinition(this->name, defn, gmaps); - else if(fs->stree->unresolvedGenericDefs[this->name].empty()) - fs->stree->unresolvedGenericDefs[this->name].push_back(this); + else if(defn->enclosingScope.stree->unresolvedGenericDefs[this->name].empty()) + defn->enclosingScope.stree->unresolvedGenericDefs[this->name].push_back(this); // add to our versions. @@ -135,8 +136,7 @@ TCResult ast::FuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con // if we have placeholders, don't bother generating anything. if(!defn->type->containsPlaceholders()) { - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(defn->id.scope); + fs->teleportInto(defn->enclosingScope); fs->enterFunctionBody(defn); fs->pushTree(defn->id.mangledName()); @@ -147,7 +147,7 @@ TCResult ast::FuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con { auto vd = util::pool(arg.loc); vd->id = Identifier(arg.name, IdKind::Name); - vd->id.scope = fs->getCurrentScope(); + vd->id.scope2 = fs->getCurrentScope2(); vd->type = arg.type; @@ -167,7 +167,7 @@ TCResult ast::FuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con fs->popTree(); fs->leaveFunctionBody(); - fs->teleportToScope(oldscope); + fs->teleportOut(); // ok, do the check. defn->needReturnVoid = !fs->checkAllPathsReturn(defn); @@ -311,7 +311,6 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) { if(auto p = dcast(Parameterisable, stmt); p) { - p->realScope = fs->getCurrentScope(); p->enclosingScope = fs->getCurrentScope2(); } @@ -327,7 +326,6 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) { if(auto p = dcast(Parameterisable, dstmt); p) { - p->realScope = fs->getCurrentScope(); p->enclosingScope = fs->getCurrentScope2(); } diff --git a/source/typecheck/misc.cpp b/source/typecheck/misc.cpp index 4e696d7a..c13272e0 100644 --- a/source/typecheck/misc.cpp +++ b/source/typecheck/misc.cpp @@ -29,14 +29,14 @@ TCResult ast::PlatformDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) defn->type = ty; defn->id = Identifier(this->typeName, IdKind::Type); - defn->id.scope = fs->getCurrentScope(); + defn->id.scope2 = fs->getCurrentScope2(); fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); // add it first so we can use it in the method bodies, // and make pointers to it { - fs->getTreeOfScope(defn->id.scope)->addDefinition(this->typeName, defn, { }); + fs->stree->addDefinition(this->typeName, defn); fs->typeDefnMap[ty] = defn; } diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index 790b7119..0caf65a5 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -33,16 +33,6 @@ void checkTraitConformity(sst::TypecheckState* fs, sst::TypeDefn* defn); - - - - - - - - - - TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* infer, const TypeParamMap_t& gmaps) { fs->pushLoc(this); @@ -59,13 +49,18 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope = this->realScope; + defn->id.scope2 = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; + defn->enclosingScope = this->enclosingScope; + defn->innerScope = this->enclosingScope.appending(defnname); // make all our methods be methods for(auto m : this->methods) - m->parentType = this, m->realScope = this->realScope + defn->id.name; + { + m->parentType = this; + m->enclosingScope = defn->innerScope; + } auto str = fir::StructType::createWithoutBody(defn->id, /* isPacked: */ this->attrs.has(attr::PACKED)); defn->type = str; @@ -76,7 +71,7 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type // add it first so we can use it in the method bodies, // and make pointers to it { - fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + this->enclosingScope.stree->addDefinition(defnname, defn, gmaps); fs->typeDefnMap[str] = defn; } @@ -115,14 +110,10 @@ TCResult ast::StructDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, c str->addTraitImpl(tdef->type->toTraitType()); } - - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(defn->id.scope); - fs->pushTree(defn->id.name); + fs->teleportInto(defn->innerScope); std::vector> tys; - fs->pushSelfContext(str); { for(auto f : this->fields) @@ -169,9 +160,7 @@ TCResult ast::StructDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, c str->setBody(tys); - fs->popTree(); - fs->teleportToScope(oldscope); - + fs->teleportOut(); checkTraitConformity(fs, defn); diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 9f56ba19..93201f39 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -134,16 +134,11 @@ namespace sst iceAssert(insertPoint); - // insertPoint->imports.push_back(import->base); mergeExternalTree(insertPoint, import->base); if(ithing.pubImport) insertPoint->reexports.push_back(import->base); - // _addTreeToExistingTree(fs->dtree->thingsImported, insertPoint, import->base, - // /* commonParent: */ nullptr, ithing.pubImport, - // /* ignoreVis: */ false, file.name); - fs->dtree->thingsImported.insert(ithing.name); fs->dtree->typeDefnMap.insert(import->typeDefnMap.begin(), import->typeDefnMap.end()); @@ -173,11 +168,15 @@ namespace sst return fs->dtree; } + static OsStrings getOsStrings() { // TODO: handle cygwin/msys/mingw??? // like how do we want to expose these? at the end of the day the os is still windows... + // TODO: this should be set for the target we are compiling FOR, so it definitely + // cannot be done using ifdefs!!!!!!!!!! + OsStrings ret; #if defined(_WIN32) @@ -279,7 +278,6 @@ static void visitDeclarables(sst::TypecheckState* fs, ast::TopLevelBlock* top) { if(auto decl = dcast(ast::Parameterisable, stmt)) { - decl->realScope = fs->getCurrentScope(); decl->enclosingScope = fs->getCurrentScope2(); decl->generateDeclaration(fs, 0, { }); } diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index 0846b248..1d908c9a 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -26,19 +26,19 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope = this->realScope; + defn->id.scope2 = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; + defn->enclosingScope = this->enclosingScope; + defn->innerScope = this->enclosingScope.appending(defnname); // make all our methods be methods for(auto m : this->methods) { m->parentType = this; - m->realScope = this->realScope + defn->id.name; - // m->enclosingScope = + m->enclosingScope = defn->innerScope; } - auto str = fir::TraitType::create(defn->id); defn->type = str; @@ -48,7 +48,7 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* // add it first so we can use it in the method bodies, // and make pointers to it { - fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + defn->enclosingScope.stree->addDefinition(defnname, defn, gmaps); fs->typeDefnMap[str] = defn; } @@ -74,11 +74,7 @@ TCResult ast::TraitDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co auto trt = defn->type->toTraitType(); iceAssert(trt); - - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(defn->id.scope); - fs->pushTree(defn->id.name); - + fs->teleportInto(defn->innerScope); fs->pushSelfContext(trt); std::vector> meths; @@ -102,9 +98,7 @@ TCResult ast::TraitDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co trt->setMethods(meths); fs->popSelfContext(); - - fs->popTree(); - fs->teleportToScope(oldscope); + fs->teleportOut(); this->finishedTypechecking.insert(defn); return TCResult(defn); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 2b83b79d..1a30f49f 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -205,93 +205,6 @@ namespace sst return zfu::join(std::vector(scope.begin(), scope.end()), "::"); } - std::vector TypecheckState::getCurrentScope() - { - std::deque scope; - StateTree* tree = this->stree; - - while(tree) - { - scope.push_front(tree->name); - tree = tree->parent; - } - - return std::vector(scope.begin(), scope.end()); - } - - StateTree* TypecheckState::getTreeOfScope(const std::vector& scope) - { - StateTree* tree = this->stree; - while(tree->parent) - tree = tree->parent; - - // ok, we should be at the topmost level now - iceAssert(tree); - - //! we're changing the behaviour subtly from how it used to function. - //* previously, we would always skip the first 'scope', under the assumption that it would be the name of the current module anyway. - //* however, the new behaviour is that, if the number of scopes passed in is 1 (one), we teleport directly to that scope, assuming an - //* implied module scope. - - //* if the number of scopes passed is 0, we teleport to the top level scope (as we do now). - - // TODO: investigate if this is the right thing to do...? - - if(scope.empty()) - { - return tree; - } - // else if(scope.size() == 1) - // { - // auto s = scope[0]; - // //* note: if our size is 1, we should check if s == toplevel_name -- if so, then we're declaring - // //* things in the global scope -- which is allowed! - - // if(s == tree->name) - // return tree; - - - // if(auto it = tree->subtrees.find(s); it == tree->subtrees.end()) - // { - // error(this->loc(), "no such tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, zfu::join(scope, "::")); - // } - // else - // { - // return it->second; - // } - // } - - - for(size_t i = 0; i < scope.size(); i++) - { - auto s = scope[i]; - - //* note: if our size is 1, we should check if s == toplevel_name -- if so, then we're declaring - //* things in the global scope -- which is allowed! - - if(i == 0 && s == tree->name) - continue; - - if(auto it = tree->subtrees.find(s); it == tree->subtrees.end()) - { - error(this->loc(), "nonexistent tree '%s' in scope '%s' (in teleportation to '%s')", s, tree->name, - zfu::join(scope, "::")); - } - else - { - tree = it->second; - } - } - - return tree; - } - - void TypecheckState::teleportToScope(const std::vector& scope) - { - this->stree = this->getTreeOfScope(scope); - } - - util::hash_map> StateTree::getAllDefinitions() { return this->definitions2; @@ -351,14 +264,18 @@ namespace sst std::string Scope::string() const { - return zfu::join(this->getStrings(), "::"); + return zfu::join(this->components(), "::"); } - std::vector Scope::getStrings() const + const std::vector& Scope::components() const { - std::vector ret; + if(!this->cachedComponents.empty()) + return this->cachedComponents; + + auto& ret = this->cachedComponents; + const Scope* s = this; - while(s) + while(s && s->stree) { ret.push_back(s->stree->name); s = s->prev; @@ -398,30 +315,6 @@ namespace sst this->teleportationStack.pop_back(); } - - - - - - - - - // TODO: maybe cache this someday? - std::vector StateTree::getScope() - { - std::deque ret; - ret.push_front(this->name); - - auto tree = this->parent; - while(tree) - { - ret.push_front(tree->name); - tree = tree->parent; - } - - return std::vector(ret.begin(), ret.end()); - } - StateTree* StateTree::searchForName(const std::string& name) { auto tree = this->parent; diff --git a/source/typecheck/unions.cpp b/source/typecheck/unions.cpp index 1ab94a90..4dc98278 100644 --- a/source/typecheck/unions.cpp +++ b/source/typecheck/unions.cpp @@ -37,18 +37,19 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope = this->realScope; + defn->id.scope2 = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; + defn->enclosingScope = this->enclosingScope; + defn->innerScope = this->enclosingScope.appending(defnname); if(israw) defn->type = fir::RawUnionType::createWithoutBody(defn->id); else defn->type = fir::UnionType::createWithoutBody(defn->id); - if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) return TCResult(err); - fs->getTreeOfScope(this->realScope)->addDefinition(defnname, defn, gmaps); + defn->enclosingScope.stree->addDefinition(defnname, defn, gmaps); this->genericVersions.push_back({ defn, fs->getGenericContextStack() }); @@ -70,17 +71,14 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co if(this->finishedTypechecking.find(tcr.defn()) != this->finishedTypechecking.end()) return TCResult(tcr.defn()); - auto oldscope = fs->getCurrentScope(); - fs->teleportToScope(tcr.defn()->id.scope); - fs->pushTree(tcr.defn()->id.name); - - sst::TypeDefn* ret = 0; if(this->attrs.has(attr::RAW)) { auto defn = dcast(sst::RawUnionDefn, tcr.defn()); iceAssert(defn); + fs->teleportInto(defn->innerScope); + //* in many ways raw unions resemble structs rather than tagged unions //* and since we are using sst::StructFieldDefn for the variants, we will need //* to enter the struct body. @@ -163,6 +161,8 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co auto defn = dcast(sst::UnionDefn, tcr.defn()); iceAssert(defn); + fs->teleportInto(defn->innerScope); + util::hash_map> vars; std::vector> vdefs; @@ -179,7 +179,7 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co vdef->parentUnion = defn; vdef->variantName = variant.first; vdef->id = Identifier(defn->id.name + "::" + variant.first, IdKind::Name); - vdef->id.scope = fs->getCurrentScope(); + vdef->id.scope2 = fs->getCurrentScope2(); vdefs.push_back({ vdef, std::get<0>(variant.second) }); @@ -201,8 +201,7 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co iceAssert(ret); this->finishedTypechecking.insert(ret); - fs->popTree(); - fs->teleportToScope(oldscope); + fs->teleportOut(); return TCResult(ret); } diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index 579aa217..5b217bd7 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -61,7 +61,6 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(this->useAs == "_") { - // TODO: check scope merge conflicts sst::mergeExternalTree(fs->stree, scopes.stree); } else diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index ad97ee02..2ad2dfd4 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -194,11 +194,11 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) sst::StateTree* tree = fs->stree; while(tree) { - // check if there is a subtree with this name. - if(auto it = tree->subtrees.find(this->name); it != tree->subtrees.end()) - return TCResult(makeScopeExpr(it->second->getScope2())); - - if(auto vs = tree->getDefinitionsWithName(this->name); vs.size() > 1) + if(auto vs = tree->getDefinitionsWithName(this->name); vs.size() == 1) + { + return checkPotentialCandidate(fs, this, vs[0], infer); + } + else if(vs.size() > 1) { std::vector> ambigs; @@ -249,10 +249,6 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) } } } - else if(!vs.empty()) - { - return checkPotentialCandidate(fs, this, vs[0], infer); - } else if(auto gdefs = tree->getUnresolvedGenericDefnsWithName(this->name); gdefs.size() > 0) { std::vector fake; @@ -278,6 +274,11 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) } } + // check if there is a subtree with this name. + if(auto it = tree->subtrees.find(this->name); it != tree->subtrees.end()) + return TCResult(makeScopeExpr(it->second->getScope2())); + + if(this->traverseUpwards) tree = tree->parent; @@ -325,7 +326,7 @@ TCResult ast::VarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) defn->attrs = this->attrs; defn->id = Identifier(this->name, IdKind::Name); - defn->id.scope = fs->getCurrentScope(); + defn->id.scope2 = fs->getCurrentScope2(); defn->immutable = this->immut; defn->visibility = this->visibility; From e4109279a93ed97ec8075b0a01da57280bf5c3e6 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 30 Nov 2020 17:43:35 +0800 Subject: [PATCH 117/129] more fixes to stuff --- build/tests/basic.flx | 2 +- build/tests/intlimits.flx | 18 +++++------ build/tmp/repro_1.flx | 26 +++++++++++----- build/tmp/repro_3.flx | 12 ++++++++ libs/std/io.flx | 4 +-- libs/std/limits.flx | 1 - makefile | 2 +- notes.md | 5 +++ source/include/typecheck.h | 3 +- source/misc/identifier.cpp | 15 +++++++-- source/typecheck/dotop.cpp | 5 ++- source/typecheck/toplevel.cpp | 21 ++++++++----- source/typecheck/typecheckstate.cpp | 48 ++++++++++++++++++----------- source/typecheck/variable.cpp | 1 - 14 files changed, 110 insertions(+), 53 deletions(-) create mode 100644 build/tmp/repro_3.flx diff --git a/build/tests/basic.flx b/build/tests/basic.flx index ed3af988..eab52c91 100644 --- a/build/tests/basic.flx +++ b/build/tests/basic.flx @@ -2,7 +2,7 @@ // Copyright (c) 2019, zhiayang, Apache License 2.0. export test_basic -import libc as _ +// import libc as _ import std::io as _ // TODO: reorganise these tests if possible diff --git a/build/tests/intlimits.flx b/build/tests/intlimits.flx index 5f5a9be5..bb35aca3 100644 --- a/build/tests/intlimits.flx +++ b/build/tests/intlimits.flx @@ -9,15 +9,15 @@ import std::limits public fn printIntegerLimits() { - // libc::printf(" i8::min = %hhd\t\t\t i8::max = %hhd\n", std::limits::int8::min, std::limits::int8::max); - // libc::printf("i16::min = %hd\t\t\ti16::max = %hd\n", std::limits::int16::min, std::limits::int16::max); - // libc::printf("i32::min = %d\t\t\ti32::max = %d\n", std::limits::int32::min, std::limits::int32::max); - // libc::printf("i64::min = %lld\ti64::max = %lld\n", std::limits::int64::min, std::limits::int64::max); + libc::printf(" i8::min = %hhd\t\t\t i8::max = %hhd\n", std::limits::int8::min, std::limits::int8::max); + libc::printf("i16::min = %hd\t\t\ti16::max = %hd\n", std::limits::int16::min, std::limits::int16::max); + libc::printf("i32::min = %d\t\t\ti32::max = %d\n", std::limits::int32::min, std::limits::int32::max); + libc::printf("i64::min = %lld\ti64::max = %lld\n", std::limits::int64::min, std::limits::int64::max); - // libc::printf("\n") + libc::printf("\n") - // libc::printf(" u8::min = %hhu\t\t\t\t u8::max = %hhu\n", std::limits::uint8::min,std::limits:: uint8::max); - // libc::printf("u16::min = %hu\t\t\t\tu16::max = %hu\n", std::limits::uint16::min, std::limits::uint16::max); - // libc::printf("u32::min = %u\t\t\t\tu32::max = %u\n", std::limits::uint32::min, std::limits::uint32::max); - // libc::printf("u64::min = %llu\t\t\t\tu64::max = %llu\n", std::limits::uint64::min, std::limits::uint64::max); + libc::printf(" u8::min = %hhu\t\t\t\t u8::max = %hhu\n", std::limits::uint8::min, std::limits::uint8::max); + libc::printf("u16::min = %hu\t\t\t\tu16::max = %hu\n", std::limits::uint16::min, std::limits::uint16::max); + libc::printf("u32::min = %u\t\t\t\tu32::max = %u\n", std::limits::uint32::min, std::limits::uint32::max); + libc::printf("u64::min = %llu\t\t\t\tu64::max = %llu\n", std::limits::uint64::min, std::limits::uint64::max); } diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index 8c0bc5f3..e0a126d3 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -2,17 +2,14 @@ // Copyright (c) 2019, zhiayang // Licensed under the Apache License Version 2.0. +/* import libc as _ -import repro_2 as _ +// import repro_2 as _ +import repro_2 +import std::limits as lim -// using repro_2 as r +using repro_2 as _ -// namespace omega -// { -// let foo: int = 30 -// } - -// let omega: str = "owo" @entry fn main() { @@ -42,6 +39,19 @@ import repro_2 as _ printf("%d\n", Bar::bla(3)); printf("lmao\n"); + let x = lim::int16::max; + printf("x = %x\n", x); + // let c = "a" + "b" } +*/ + +import std::io as _ +import repro_3 + +@entry fn main2() +{ + println("owo") + uwu::owo::foozle() +} diff --git a/build/tmp/repro_3.flx b/build/tmp/repro_3.flx new file mode 100644 index 00000000..9c54e09c --- /dev/null +++ b/build/tmp/repro_3.flx @@ -0,0 +1,12 @@ +// repro_3.flx +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +export uwu::owo + +ffi fn printf(fmt: &i8, ...) -> i32 + +public fn foozle() +{ + printf("asdf\n"); +} diff --git a/libs/std/io.flx b/libs/std/io.flx index 1952c13a..241828a5 100644 --- a/libs/std/io.flx +++ b/libs/std/io.flx @@ -4,5 +4,5 @@ export std::io -public import file -public import print +public import file as _ +public import print as _ diff --git a/libs/std/limits.flx b/libs/std/limits.flx index 4d0d5b2e..6caecc5b 100644 --- a/libs/std/limits.flx +++ b/libs/std/limits.flx @@ -3,7 +3,6 @@ // Licensed under the Apache License Version 2.0. export std::limits -import libc public namespace int8 { diff --git a/makefile b/makefile index 0a973048..6e13322d 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ -WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wdeprecated-declarations +WARNINGS := -Wno-unused-parameter -Wno-sign-conversion -Wno-padded -Wno-conversion -Wno-shadow -Wno-missing-noreturn -Wno-unused-macros -Wno-switch-enum -Wno-deprecated -Wno-format-nonliteral -Wno-trigraphs -Wno-unused-const-variable -Wdeprecated-declarations -Werror=return-type GCCWARNINGS := -Wno-init-list-lifetime diff --git a/notes.md b/notes.md index 91bd401f..7d9d7fe6 100644 --- a/notes.md +++ b/notes.md @@ -29,6 +29,11 @@ the imports list of the current scope. +## to fix + +1. `public static` doesn't work, but `static public` works. + + ## to refactor 2. a lot of places probably still have the concept of `scope == std::vector`. diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 72e1b209..fbf7f8da 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -73,6 +73,7 @@ namespace sst util::hash_map> definitions2; + StateTree* proxyOf = nullptr; std::vector exports; std::vector imports; std::vector reexports; @@ -88,7 +89,7 @@ namespace sst Scope cachedScope; const Scope& getScope2(); - StateTree* searchForName(const std::string& name); + StateTree* findSubtree(const std::string& name); StateTree* findOrCreateSubtree(const std::string& name, bool anonymous = false); util::hash_map> getAllDefinitions(); diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 442730c2..7b14590b 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -12,7 +12,10 @@ sst::Stmt* TCResult::stmt() const { if(this->_kind == RK::Error) - throw ErrorException(this->_pe); //this->_pe->postAndQuit(); + { + this->_pe->postAndQuit(); + // throw ErrorException(this->_pe); + } switch(this->_kind) { @@ -26,7 +29,10 @@ sst::Stmt* TCResult::stmt() const sst::Expr* TCResult::expr() const { if(this->_kind == RK::Error) - throw ErrorException(this->_pe); // this->_pe->postAndQuit(); + { + this->_pe->postAndQuit(); + // throw ErrorException(this->_pe); + } if(this->_kind != RK::Expression) _error_and_exit("not expr\n"); @@ -37,7 +43,10 @@ sst::Expr* TCResult::expr() const sst::Defn* TCResult::defn() const { if(this->_kind == RK::Error) - throw ErrorException(this->_pe); //this->_pe->postAndQuit(); + { + this->_pe->postAndQuit(); + // throw ErrorException(this->_pe); + } if(this->_kind != RK::Definition) _error_and_exit("not defn\n"); diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index 56534994..e73cc827 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -786,7 +786,10 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, def = fs->typeDefnMap[te->type]; } - iceAssert(def); + if(!def) + { + error(left->loc, "could not resolve definition"); + } if(auto typdef = dcast(sst::TypeDefn, def)) { diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 93201f39..8fbba9e8 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -54,19 +54,25 @@ namespace sst void mergeExternalTree(sst::StateTree* base, sst::StateTree* branch) { + if(branch->isAnonymous || branch->isCompilerGenerated) + return; + // first check conflicts for this level: checkConflictingDefinitions(base, branch); // no problem -- attach the trees base->imports.push_back(branch); - // check recursively - // TODO: optimise this by looping over the tree with less subtrees and doing hash lookup - // on the one with more subtrees. - for(const auto& [ name, sub ] : base->subtrees) + if(auto proxy = branch->proxyOf) + base->imports.push_back(proxy); + + // merge the subtrees as well. + for(const auto& [ name, tr ] : branch->subtrees) { - if(auto it = branch->subtrees.find(name); it != branch->subtrees.end()) - mergeExternalTree(sub, it->second); + if(tr->isCompilerGenerated || tr->isAnonymous) + continue; + + mergeExternalTree(base->findOrCreateSubtree(name), tr); } } @@ -90,7 +96,7 @@ namespace sst for(auto [ ithing, import ] : imports) { - info(ithing.loc, "import: %s", ithing.name); + info(ithing.loc, "(%s) import: %s", file.name, ithing.name); auto ias = ithing.importAs; if(ias.empty()) @@ -130,6 +136,7 @@ namespace sst } insertPoint = curinspt; + insertPoint->proxyOf = import->base; } iceAssert(insertPoint); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 1a30f49f..fde9ed2d 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -3,6 +3,7 @@ // Licensed under the Apache License Version 2.0. #include "ast.h" +#include "defs.h" #include "errors.h" #include "typecheck.h" @@ -10,7 +11,9 @@ #include "memorypool.h" #include "zfu.h" +#include #include +#include namespace sst { @@ -210,27 +213,34 @@ namespace sst return this->definitions2; } - static void fetchDefinitionsFrom(const std::string& name, StateTree* tree, bool recursively, std::vector& out) + static void fetchDefinitionsFrom(const std::string& name, StateTree* tree, bool recursively, bool includePrivate, std::vector& out) { if(auto it = tree->definitions2.find(name); it != tree->definitions2.end()) - out.insert(out.end(), it->second.begin(), it->second.end()); - - if(!recursively) - return; + { + std::copy_if(it->second.begin(), it->second.end(), std::back_inserter(out), [includePrivate](Defn* defn) -> bool { + return (includePrivate ? true : defn->visibility == VisibilityLevel::Public); + }); + } for(auto import : tree->imports) { - fetchDefinitionsFrom(name, import, false, out); + if(recursively) + { + // only include private things if we're in the same file. + bool priv = tree->topLevelFilename == import->topLevelFilename; + fetchDefinitionsFrom(name, import, /* recursively: */ false, /* includePrivate: */ priv, out); + } + // in theory we should never include the private definitions from re-exports for(auto reexp : import->reexports) - fetchDefinitionsFrom(name, reexp, false, out); + fetchDefinitionsFrom(name, reexp, /* recursively: */ false, /* includePrivate: */ false, out); } } std::vector StateTree::getDefinitionsWithName(const std::string& name) { std::vector ret; - fetchDefinitionsFrom(name, this, true, ret); + fetchDefinitionsFrom(name, this, /* recursively: */ true, /* includePrivate: */ true, ret); return ret; } @@ -246,8 +256,6 @@ namespace sst void StateTree::addDefinition(const std::string& sourceFile, const std::string& name, Defn* def, const TypeParamMap_t& gmaps) { - // this->definitions[sourceFile][util::typeParamMapToString(name, gmaps)].push_back(def); - // this->definitions[sourceFile].defns[name].push_back(def); this->definitions2[name].push_back(def); } @@ -315,18 +323,23 @@ namespace sst this->teleportationStack.pop_back(); } - StateTree* StateTree::searchForName(const std::string& name) + StateTree* StateTree::findSubtree(const std::string& name) { - auto tree = this->parent; - while(tree) + if(auto it = this->subtrees.find(name); it != this->subtrees.end()) + return it->second; + + // check our imports, and our imports' reexports. + for(const auto imp : this->imports) { - if(tree->name == name) - return tree; + if(imp->name == name) + return imp; - tree = tree->parent; + for(const auto exp : imp->reexports) + if(exp->name == name) + return exp; } - return 0; + return nullptr; } StateTree* StateTree::findOrCreateSubtree(const std::string& name, bool anonymous) @@ -363,7 +376,6 @@ namespace sst if(fns.size() > 0) return fns; - // ret.insert(ret.end(), fns.begin(), fns.end()); tree = tree->parent; } diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 2ad2dfd4..77e7c2d1 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -278,7 +278,6 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(auto it = tree->subtrees.find(this->name); it != tree->subtrees.end()) return TCResult(makeScopeExpr(it->second->getScope2())); - if(this->traverseUpwards) tree = tree->parent; From dd41c0b80e9f76ab431c0e0a737273bed5ca6b1b Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 30 Nov 2020 17:59:47 +0800 Subject: [PATCH 118/129] remove proxyOf mechanism, since it was not necessary --- build/tests/basic.flx | 2 +- build/tmp/repro_1.flx | 14 ++++++++++---- build/tmp/repro_3.flx | 9 +-------- source/include/typecheck.h | 1 - source/typecheck/toplevel.cpp | 8 ++++---- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/build/tests/basic.flx b/build/tests/basic.flx index eab52c91..ed3af988 100644 --- a/build/tests/basic.flx +++ b/build/tests/basic.flx @@ -2,7 +2,7 @@ // Copyright (c) 2019, zhiayang, Apache License 2.0. export test_basic -// import libc as _ +import libc as _ import std::io as _ // TODO: reorganise these tests if possible diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index e0a126d3..1f6e8309 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -46,12 +46,18 @@ using repro_2 as _ } */ +import libc as _ +import repro_3 as _ + +// import std::io +// import repro_3 -import std::io as _ -import repro_3 +// using std::io as owo @entry fn main2() { - println("owo") - uwu::owo::foozle() + // std::io::println("owo") + // owo::println("owo") + // uwu::owo::foozle() + printf("owo\n") } diff --git a/build/tmp/repro_3.flx b/build/tmp/repro_3.flx index 9c54e09c..667bdfa5 100644 --- a/build/tmp/repro_3.flx +++ b/build/tmp/repro_3.flx @@ -2,11 +2,4 @@ // Copyright (c) 2020, zhiayang // Licensed under the Apache License Version 2.0. -export uwu::owo - -ffi fn printf(fmt: &i8, ...) -> i32 - -public fn foozle() -{ - printf("asdf\n"); -} +public import libc as _ diff --git a/source/include/typecheck.h b/source/include/typecheck.h index fbf7f8da..032a3b38 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -73,7 +73,6 @@ namespace sst util::hash_map> definitions2; - StateTree* proxyOf = nullptr; std::vector exports; std::vector imports; std::vector reexports; diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 8fbba9e8..19fbd68b 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -24,11 +24,13 @@ namespace sst std::string name; std::string vendor; }; + static OsStrings getOsStrings(); static void generatePreludeDefinitions(TypecheckState* fs); static bool definitionsConflict(const sst::Defn* a, const sst::Defn* b) { + info("dupe check: %s, %s", a->loc.shortString(), b->loc.shortString()); return false; } @@ -36,8 +38,10 @@ namespace sst { for(const auto& [ name, defns ] : base->definitions2) { + zpr::println("check %s", name); if(auto it = branch->definitions2.find(name); it != branch->definitions2.end()) { + zpr::println("found something"); for(auto d1 : defns) { for(auto d2 : it->second) @@ -63,9 +67,6 @@ namespace sst // no problem -- attach the trees base->imports.push_back(branch); - if(auto proxy = branch->proxyOf) - base->imports.push_back(proxy); - // merge the subtrees as well. for(const auto& [ name, tr ] : branch->subtrees) { @@ -136,7 +137,6 @@ namespace sst } insertPoint = curinspt; - insertPoint->proxyOf = import->base; } iceAssert(insertPoint); From 52fdf6fc9f26175fdd1bbb6ab9e5389452fecaf7 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 30 Nov 2020 19:42:56 +0800 Subject: [PATCH 119/129] check for dupes, and add a good error message. --- source/frontend/import.cpp | 9 ++-- source/frontend/parser/toplevel.cpp | 10 ++-- source/include/defs.h | 5 ++ source/include/parser_internal.h | 2 +- source/include/typecheck.h | 26 +++++----- source/typecheck/arithmetic.cpp | 2 +- source/typecheck/function.cpp | 2 +- source/typecheck/operators.cpp | 2 +- source/typecheck/resolver/driver.cpp | 3 -- source/typecheck/resolver/misc.cpp | 13 +++-- source/typecheck/toplevel.cpp | 72 +++++++++++++++++++++++----- source/typecheck/typecheckstate.cpp | 37 ++------------ source/typecheck/using.cpp | 4 +- source/typecheck/variable.cpp | 6 +-- 14 files changed, 110 insertions(+), 83 deletions(-) diff --git a/source/frontend/import.cpp b/source/frontend/import.cpp index 121c5735..a90d0de4 100644 --- a/source/frontend/import.cpp +++ b/source/frontend/import.cpp @@ -92,22 +92,23 @@ namespace parser i++; - Location impLoc; std::string name; + Location impLoc = tok.loc; std::vector impAs; if(tokens[i] == TT::StringLiteral) { name = tokens[i].str(); - impLoc = tokens[i].loc; + impLoc = impLoc.unionWith(tokens[i].loc); i++; } else if(tokens[i] == TT::Identifier) { - std::vector bits = parseIdentPath(tokens, &i); + auto [ loc, bits ] = parseIdentPath(tokens, &i); //* we concatanate the thing, using '/' as the path separator, and appending '.flx' to the end. name = zfu::join(bits, "/") + ".flx"; + impLoc = impLoc.unionWith(loc); } else { @@ -119,7 +120,7 @@ namespace parser { i++; if(tokens[i] == TT::Identifier) - impAs = parseIdentPath(tokens, &i); + impAs = parseIdentPath(tokens, &i).second; else expectedAfter(tokens[i - 1].loc, "identifier", "'import-as'", tokens[i - 1].str()); diff --git a/source/frontend/parser/toplevel.cpp b/source/frontend/parser/toplevel.cpp index 3a45311e..c2bf85d9 100644 --- a/source/frontend/parser/toplevel.cpp +++ b/source/frontend/parser/toplevel.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. +#include "defs.h" #include "pts.h" #include "errors.h" #include "parser.h" @@ -16,14 +17,17 @@ using namespace ast; namespace parser { - std::vector parseIdentPath(const lexer::TokenList& tokens, size_t* idx) + std::pair> parseIdentPath(const lexer::TokenList& tokens, size_t* idx) { using TT = lexer::TokenType; std::vector path; size_t i = *idx; + Location loc = tokens[i].loc; while(tokens[i] == TT::Identifier) { + loc = loc.unionWith(tokens[i].loc); + path.push_back(tokens[i].str()); i++; @@ -40,7 +44,7 @@ namespace parser } *idx = i; - return path; + return { loc, path }; } @@ -63,7 +67,7 @@ namespace parser if(tokens[i].type == TT::Identifier) { - path = parseIdentPath(tokens, &i); + path = parseIdentPath(tokens, &i).second; } else { diff --git a/source/include/defs.h b/source/include/defs.h index 1e343d0e..0d998344 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -199,6 +199,11 @@ struct Location return !(*this == other); } + Location unionWith(const Location& x) const + { + return unionOf(*this, x); + } + static Location unionOf(const Location& a, const Location& b) { if(a.fileID != b.fileID || a.line != b.line) diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 12d5c782..189f6a11 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -498,7 +498,7 @@ namespace parser std::tuple, std::vector>, pts::Type*, bool, Location> parseFunctionLookingDecl(State& st); - std::vector parseIdentPath(const lexer::TokenList& tokens, size_t* idx); + std::pair> parseIdentPath(const lexer::TokenList& tokens, size_t* idx); } diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 032a3b38..39f968a1 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -77,6 +77,11 @@ namespace sst std::vector imports; std::vector reexports; + // this is a mapping from every StateTree in `imports` to the location of the `import` or `using` statement. + // it only used for error-reporting, so it is stored out-of-line so the usage of `this->imports` is not more + // cumbersome than it needs to be. + std::map> importMetadata; + // what's there to explain? a simple map of operators to their functions. we use // function overload resolution to determine which one to call, and ambiguities are @@ -86,7 +91,7 @@ namespace sst util::hash_map> postfixOperatorOverloads; Scope cachedScope; - const Scope& getScope2(); + const Scope& getScope(); StateTree* findSubtree(const std::string& name); StateTree* findOrCreateSubtree(const std::string& name, bool anonymous = false); @@ -98,11 +103,6 @@ namespace sst void addDefinition(const std::string& name, Defn* def, const TypeParamMap_t& gmaps = { }); void addDefinition(const std::string& sourceFile, const std::string& name, Defn* def, const TypeParamMap_t& gmaps = { }); - - - - // TODO: remove this! for debugging only. - void dump(); }; struct DefinitionTree @@ -206,27 +206,25 @@ namespace sst fir::Type* convertParserTypeToFIR(pts::Type* pt, bool allowFailure = false); fir::Type* inferCorrectTypeForLiteral(fir::ConstantNumberType* lit); - fir::Type* checkIsBuiltinConstructorCall(const std::string& name, const std::vector& arguments); - bool checkAllPathsReturn(FunctionDefn* fn); std::pair, SimpleError> verifyStructConstructorArguments(const std::string& name, const std::set& fieldNames, const std::vector& params); DecompMapping typecheckDecompositions(const DecompMapping& bind, fir::Type* rhs, bool immut, bool allowref); - - int getOverloadDistance(const std::vector& a, const std::vector& b); - bool isDuplicateOverload(const std::vector& a, const std::vector& b); }; + + + bool isDuplicateOverload(const std::vector& a, const std::vector& b); + int getOverloadDistance(const std::vector& a, const std::vector& b); + DefinitionTree* typecheck(frontend::CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports, bool addPreludeDefinitions); - - void mergeExternalTree(sst::StateTree* base, sst::StateTree* branch); - std::vector collateGenericArgStacks(); + void mergeExternalTree(const Location& importer, const char* kind, sst::StateTree* base, sst::StateTree* branch); } diff --git a/source/typecheck/arithmetic.cpp b/source/typecheck/arithmetic.cpp index bd3b91a3..5ebe56ec 100644 --- a/source/typecheck/arithmetic.cpp +++ b/source/typecheck/arithmetic.cpp @@ -24,7 +24,7 @@ static sst::FunctionDefn* getOverloadedOperator(sst::TypecheckState* fs, const L for(auto ovp : (*thelist)[op]) { - int dist = fs->getOverloadDistance(zfu::map(ovp->params, [](const auto& p) { return p.type; }), args); + int dist = sst::getOverloadDistance(zfu::map(ovp->params, [](const auto& p) { return p.type; }), args); if(dist == -1) continue; if(dist == best) diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 9d35f6a1..8e6640ef 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -94,7 +94,7 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* { // make sure we didn't fuck up somewhere iceAssert(oth->id.name == defn->id.name); - return fs->isDuplicateOverload(defn->params, oth->params); + return sst::isDuplicateOverload(defn->params, oth->params); } else { diff --git a/source/typecheck/operators.cpp b/source/typecheck/operators.cpp index ff6b033d..7327343b 100644 --- a/source/typecheck/operators.cpp +++ b/source/typecheck/operators.cpp @@ -162,7 +162,7 @@ TCResult ast::OperatorOverloadDefn::generateDeclaration(sst::TypecheckState* fs, for(auto it : (*thelist)[this->symbol]) { - if(fs->isDuplicateOverload(it->params, defn->params)) + if(sst::isDuplicateOverload(it->params, defn->params)) { SimpleError::make(this->loc, "duplicate operator overload for '%s' taking identical arguments", this->symbol) ->append(SimpleError::make(MsgType::Note, it->loc, "previous definition was here:")) diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 4d07478a..1e00cd2e 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -118,9 +118,6 @@ namespace resolver while(top && top->parent) top = top->parent; - // just dump this. - top->dump(); - return TCResult(SimpleError::make(fs->loc(), "no function named '%s' in the current scope", name)); } else diff --git a/source/typecheck/resolver/misc.cpp b/source/typecheck/resolver/misc.cpp index 2390e843..22e808d1 100644 --- a/source/typecheck/resolver/misc.cpp +++ b/source/typecheck/resolver/misc.cpp @@ -218,21 +218,20 @@ namespace sst - int TypecheckState::getOverloadDistance(const std::vector& a, const std::vector& b) + int getOverloadDistance(const std::vector& a, const std::vector& b) { return resolver::computeOverloadDistance(Location(), zfu::map(a, [](fir::Type* t) -> fir::LocatedType { return fir::LocatedType(t, Location()); }), zfu::map(b, [](fir::Type* t) -> fir::LocatedType { return fir::LocatedType(t, Location()); - }), /* isCVarArg: */ false, this->loc()).first; + }), /* isCVarArg: */ false, Location()).first; } - bool TypecheckState::isDuplicateOverload(const std::vector& a, const std::vector& b) + bool isDuplicateOverload(const std::vector& a, const std::vector& b) { - return this->getOverloadDistance(zfu::map(a, [](const auto& p) -> auto { return p.type; }), + return getOverloadDistance(zfu::map(a, [](const auto& p) -> auto { return p.type; }), zfu::map(b, [](const auto& p) -> auto { return p.type; })) == 0; } - } @@ -261,6 +260,10 @@ namespace sst + + + + diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index 19fbd68b..cbb2c74b 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -30,25 +30,56 @@ namespace sst static bool definitionsConflict(const sst::Defn* a, const sst::Defn* b) { - info("dupe check: %s, %s", a->loc.shortString(), b->loc.shortString()); - return false; + auto fda = dcast(const sst::FunctionDecl, a); + auto fdb = dcast(const sst::FunctionDecl, b); + + if(fda && fdb) + { + return sst::isDuplicateOverload(fda->params, fdb->params); + } + else + { + return true; + } } - static void checkConflictingDefinitions(const sst::StateTree* base, const sst::StateTree* branch) + static void checkConflictingDefinitions(Location loc, const char* kind, const sst::StateTree* base, const sst::StateTree* branch, + const sst::StateTree* rootBase = nullptr) { for(const auto& [ name, defns ] : base->definitions2) { - zpr::println("check %s", name); if(auto it = branch->definitions2.find(name); it != branch->definitions2.end()) { - zpr::println("found something"); for(auto d1 : defns) { for(auto d2 : it->second) { if(definitionsConflict(d1, d2)) { - error(d1->loc, "conflicting definitions"); + auto error = SimpleError::make(MsgType::Error, loc, "'%s' here introduces duplicate definitions:", kind); + + if(d1 == d2 || d1->loc == d2->loc) + { + if(rootBase != nullptr) + { + if(auto it = rootBase->importMetadata.find(base); it != rootBase->importMetadata.end()) + { + auto [ iloc, name ] = it->second; + error->append(SimpleError::make(MsgType::Note, iloc, "most likely, the module '%s' was " + "already brought into the current scope by this statement:", name)); + } + } + + error->append(SimpleError::make(MsgType::Note, d1->loc, "for reference, here is the (first) " + "conflicting definition:")); + } + else + { + error->append(SimpleError::make(MsgType::Note, d1->loc, "first definition here:")) + ->append(SimpleError::make(MsgType::Note, d2->loc, "second definition here:")); + } + + error->postAndQuit(); } } } @@ -56,16 +87,34 @@ namespace sst } } - void mergeExternalTree(sst::StateTree* base, sst::StateTree* branch) + void mergeExternalTree(const Location& loc, const char* kind, sst::StateTree* base, sst::StateTree* branch) { if(branch->isAnonymous || branch->isCompilerGenerated) return; // first check conflicts for this level: - checkConflictingDefinitions(base, branch); + checkConflictingDefinitions(loc, kind, base, branch); + + // then, for every one of *our* imports: + for(auto import : base->imports) + { + // check that the new tree doesn't trample over it. + checkConflictingDefinitions(loc, kind, import, branch, base); + + // then, also check that, for every one of *their* public imports: + for(auto rexp : branch->reexports) + { + // it doesn't trample with anything in our tree, + checkConflictingDefinitions(loc, kind, base, rexp); + + // and it doesn't conflict with anything in our imports. + checkConflictingDefinitions(loc, kind, import, rexp, base); + } + } // no problem -- attach the trees base->imports.push_back(branch); + base->importMetadata[branch] = { loc, branch->name }; // merge the subtrees as well. for(const auto& [ name, tr ] : branch->subtrees) @@ -73,7 +122,7 @@ namespace sst if(tr->isCompilerGenerated || tr->isAnonymous) continue; - mergeExternalTree(base->findOrCreateSubtree(name), tr); + mergeExternalTree(loc, kind, base->findOrCreateSubtree(name), tr); } } @@ -97,7 +146,7 @@ namespace sst for(auto [ ithing, import ] : imports) { - info(ithing.loc, "(%s) import: %s", file.name, ithing.name); + // info(ithing.loc, "(%s) import: %s", file.name, ithing.name); auto ias = ithing.importAs; if(ias.empty()) @@ -140,8 +189,7 @@ namespace sst } iceAssert(insertPoint); - - mergeExternalTree(insertPoint, import->base); + mergeExternalTree(ithing.loc, "import", insertPoint, import->base); if(ithing.pubImport) insertPoint->reexports.push_back(import->base); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index fde9ed2d..19159d84 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -191,7 +191,7 @@ namespace sst Scope TypecheckState::getCurrentScope2() { - return this->stree->getScope2(); + return this->stree->getScope(); } std::string TypecheckState::serialiseCurrentScope() @@ -293,14 +293,14 @@ namespace sst return ret; } - const Scope& StateTree::getScope2() + const Scope& StateTree::getScope() { if(!this->cachedScope.stree) { this->cachedScope.stree = this; if(this->parent) - this->cachedScope.prev = &this->parent->getScope2(); + this->cachedScope.prev = &this->parent->getScope(); } return this->cachedScope; @@ -308,7 +308,7 @@ namespace sst const Scope& Scope::appending(const std::string& name) const { - return this->stree->findOrCreateSubtree(name)->getScope2(); + return this->stree->findOrCreateSubtree(name)->getScope(); } void TypecheckState::teleportInto(const Scope& scope) @@ -503,35 +503,6 @@ namespace sst } - // UWU - static int indent = 0; - void StateTree::dump() - { - zpr::println("%*s* TREE: %s - %s", indent * 2, "", this->name, frontend::getFilenameFromPath(this->topLevelFilename)); - for(const auto& [ name, defs ] : this->definitions2) - { - zpr::println("%*s* %s", (indent + 1) * 2, "", name); - for(auto d : defs) - zpr::println("%*s> %s: %s", (indent + 2) * 2, "", d->id.str(), d->type ? d->type->str() : "??"); - } - - if(!this->subtrees.empty()) - { - zpr::println("\n%*s* SUBTREES:", (indent + 1) * 2, ""); - indent += 2; - for(auto& [ name, sub ] : this->subtrees) - { - if('0' <= name[0] && name[0] <= '9') - continue; - - sub->dump(); - } - - zpr::println("\n"); - indent -= 2; - } - } - Scope::Scope(StateTree* st) { this->prev = 0; diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index 5b217bd7..fba82ae0 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -61,7 +61,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(this->useAs == "_") { - sst::mergeExternalTree(fs->stree, scopes.stree); + sst::mergeExternalTree(this->loc, "using", fs->stree, scopes.stree); } else { @@ -76,7 +76,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) } auto tree = fs->stree->findOrCreateSubtree(this->useAs); - sst::mergeExternalTree(tree, scopes.stree); + sst::mergeExternalTree(this->loc, "using", tree, scopes.stree); } return TCResult::getDummy(); diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 77e7c2d1..3fc35ca4 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -161,7 +161,7 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) while(t->parent) t = t->parent; - return TCResult(makeScopeExpr(t->getScope2())); + return TCResult(makeScopeExpr(t->getScope())); } else if(this->name == "^") { @@ -177,7 +177,7 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) while(t->isAnonymous && t->parent) t = t->parent; - return TCResult(makeScopeExpr(t->getScope2())); + return TCResult(makeScopeExpr(t->getScope())); } } @@ -276,7 +276,7 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) // check if there is a subtree with this name. if(auto it = tree->subtrees.find(this->name); it != tree->subtrees.end()) - return TCResult(makeScopeExpr(it->second->getScope2())); + return TCResult(makeScopeExpr(it->second->getScope())); if(this->traverseUpwards) tree = tree->parent; From 41c2103b2af2cf30d6bf6d5cbf2306650e3a53f0 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 30 Nov 2020 23:17:21 +0800 Subject: [PATCH 120/129] remove topLevelFilename from StateTree --- build/tmp/repro_1.flx | 10 +-- notes.md | 2 + source/include/typecheck.h | 15 ++-- source/repl/execute.cpp | 2 +- source/typecheck/classes.cpp | 11 +-- source/typecheck/toplevel.cpp | 135 +++++++++++++++++++++------- source/typecheck/typecheckstate.cpp | 32 +++---- source/typecheck/using.cpp | 8 +- 8 files changed, 148 insertions(+), 67 deletions(-) diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index 1f6e8309..2cf0e699 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -10,7 +10,6 @@ import std::limits as lim using repro_2 as _ - @entry fn main() { class Foozle @@ -46,13 +45,11 @@ using repro_2 as _ } */ -import libc as _ -import repro_3 as _ -// import std::io -// import repro_3 +import libc +import repro_3 as _ -// using std::io as owo +// using libc as _ @entry fn main2() { @@ -61,3 +58,4 @@ import repro_3 as _ // uwu::owo::foozle() printf("owo\n") } + diff --git a/notes.md b/notes.md index 7d9d7fe6..8a656b13 100644 --- a/notes.md +++ b/notes.md @@ -33,6 +33,8 @@ the imports list of the current scope. 1. `public static` doesn't work, but `static public` works. +2. underlining breaks for multi-line spans; just don't underline in that case. + ## to refactor diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 39f968a1..9858d53d 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -48,11 +48,9 @@ namespace sst struct StateTree { - StateTree(const std::string& nm, const std::string& filename, StateTree* p, bool anon = false) - : name(nm), topLevelFilename(filename), parent(p), isAnonymous(anon) { } + StateTree(const std::string& nm, StateTree* p, bool anon = false) : name(nm), parent(p), isAnonymous(anon) { } std::string name; - std::string topLevelFilename; StateTree* parent = 0; @@ -77,11 +75,17 @@ namespace sst std::vector imports; std::vector reexports; + StateTree* proxyOf = nullptr; + // this is a mapping from every StateTree in `imports` to the location of the `import` or `using` statement. // it only used for error-reporting, so it is stored out-of-line so the usage of `this->imports` is not more - // cumbersome than it needs to be. + // cumbersome than it needs to be. the string in the pair stores the name of the module *IMPORTING* the module, + // not the name of the module being imported (that can be gotten from the StateTree). std::map> importMetadata; + // the same thing, but for reexports (ie. public imports). + std::map> reexportMetadata; + // what's there to explain? a simple map of operators to their functions. we use // function overload resolution to determine which one to call, and ambiguities are @@ -96,13 +100,12 @@ namespace sst StateTree* findSubtree(const std::string& name); StateTree* findOrCreateSubtree(const std::string& name, bool anonymous = false); - util::hash_map> getAllDefinitions(); + std::vector getAllDefinitions(); std::vector getDefinitionsWithName(const std::string& name); std::vector getUnresolvedGenericDefnsWithName(const std::string& name); void addDefinition(const std::string& name, Defn* def, const TypeParamMap_t& gmaps = { }); - void addDefinition(const std::string& sourceFile, const std::string& name, Defn* def, const TypeParamMap_t& gmaps = { }); }; struct DefinitionTree diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index a3c2302c..07a1bda1 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -30,7 +30,7 @@ namespace repl this->module = new fir::Module(modname); - sst::StateTree* tree = new sst::StateTree(modname, modname, 0); + sst::StateTree* tree = new sst::StateTree(modname, 0); this->fs = new sst::TypecheckState(tree); this->cs = new cgn::CodegenState(fir::IRBuilder(this->module)); this->cs->module = this->module; diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index 3634b0c0..d4f8000d 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -354,19 +354,16 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co std::function recursivelyImport = [&](sst::StateTree* from, sst::StateTree* to) -> void { - for(auto [ file, defs ] : from->getAllDefinitions()) + for(auto def : from->getAllDefinitions()) { - for(auto def : defs) - { - if(!dcast(sst::ClassInitialiserDefn, def)) - to->addDefinition(file, def->id.name, def); - } + if(!dcast(sst::ClassInitialiserDefn, def)) + to->addDefinition(def->id.name, def); } for(auto sub : from->subtrees) { if(to->subtrees.find(sub.first) == to->subtrees.end()) - to->subtrees[sub.first] = util::pool(sub.first, sub.second->topLevelFilename, to); + to->subtrees[sub.first] = util::pool(sub.first, to); recursivelyImport(sub.second, to->subtrees[sub.first]); } diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index cbb2c74b..b77c3dc5 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -26,25 +26,39 @@ namespace sst }; static OsStrings getOsStrings(); - static void generatePreludeDefinitions(TypecheckState* fs); + static void generatePreludeDefinitions(TypecheckState* fs, const std::string& filename); static bool definitionsConflict(const sst::Defn* a, const sst::Defn* b) { auto fda = dcast(const sst::FunctionDecl, a); auto fdb = dcast(const sst::FunctionDecl, b); + auto uva = dcast(const sst::UnionVariantDefn, a); + auto uvb = dcast(const sst::UnionVariantDefn, b); + if(fda && fdb) { return sst::isDuplicateOverload(fda->params, fdb->params); } + else if(uva && uvb) + { + return uva->parentUnion == uvb->parentUnion; + } else { return true; } } + struct ImportMetadata + { + Location loc; + std::string name; + std::string imported; + }; + static void checkConflictingDefinitions(Location loc, const char* kind, const sst::StateTree* base, const sst::StateTree* branch, - const sst::StateTree* rootBase = nullptr) + std::optional importer = { }, std::optional exporter = { }) { for(const auto& [ name, defns ] : base->definitions2) { @@ -54,39 +68,74 @@ namespace sst { for(auto d2 : it->second) { - if(definitionsConflict(d1, d2)) + if(!definitionsConflict(d1, d2)) + continue; + + auto error = SimpleError::make(MsgType::Error, loc, "'%s' here introduces duplicate definitions:", kind); + + if(d1 == d2) { - auto error = SimpleError::make(MsgType::Error, loc, "'%s' here introduces duplicate definitions:", kind); + if(importer && exporter) + { + error->append(SimpleError::make(MsgType::Note, exporter->loc, + "this public import (from the imported module '%s') brings '%s' into scope ...", + exporter->name, exporter->imported)); - if(d1 == d2 || d1->loc == d2->loc) + error->append(SimpleError::make(MsgType::Note, *importer, + "... which conflicts with this using/import statement here:")); + } + else if(importer) { - if(rootBase != nullptr) - { - if(auto it = rootBase->importMetadata.find(base); it != rootBase->importMetadata.end()) - { - auto [ iloc, name ] = it->second; - error->append(SimpleError::make(MsgType::Note, iloc, "most likely, the module '%s' was " - "already brought into the current scope by this statement:", name)); - } - } - - error->append(SimpleError::make(MsgType::Note, d1->loc, "for reference, here is the (first) " - "conflicting definition:")); + error->append(SimpleError::make(MsgType::Note, *importer, + "most likely caused by this import here:")); } - else + else if(exporter) { - error->append(SimpleError::make(MsgType::Note, d1->loc, "first definition here:")) - ->append(SimpleError::make(MsgType::Note, d2->loc, "second definition here:")); + error->append(SimpleError::make(MsgType::Note, exporter->loc, + "most likely caused by this public import here, in module '%s':", exporter->name)); } - error->postAndQuit(); + error->append(SimpleError::make(MsgType::Note, d1->loc, "for reference, here is the (first) " + "conflicting definition:")); } + else + { + error->append(SimpleError::make(MsgType::Note, d1->loc, "first definition here:")) + ->append(SimpleError::make(MsgType::Note, d2->loc, "second definition here:")); + } + + error->postAndQuit(); } } } } } + static std::optional getExportInfo(const sst::StateTree* base, const sst::StateTree* branch) + { + if(auto it = base->reexportMetadata.find(branch); it != base->reexportMetadata.end()) + { + return ImportMetadata { + .loc = it->second.first, + .name = it->second.second, + .imported = branch->name + }; + } + return { }; + } + + static void checkExportsRecursively(const Location& loc, const char* kind, sst::StateTree* base, sst::StateTree* branch, + std::optional importer = { }, std::optional exporter = { }) + { + if(branch->isAnonymous || branch->isCompilerGenerated) + return; + + checkConflictingDefinitions(loc, kind, base, branch, importer, exporter); + + for(auto exp : base->reexports) + checkExportsRecursively(loc, kind, exp, branch, importer, getExportInfo(base, exp)); + } + void mergeExternalTree(const Location& loc, const char* kind, sst::StateTree* base, sst::StateTree* branch) { if(branch->isAnonymous || branch->isCompilerGenerated) @@ -98,23 +147,45 @@ namespace sst // then, for every one of *our* imports: for(auto import : base->imports) { + std::optional importer; + if(auto it = base->importMetadata.find(import); it != base->importMetadata.end()) + importer = it->second.first; + // check that the new tree doesn't trample over it. - checkConflictingDefinitions(loc, kind, import, branch, base); + checkConflictingDefinitions(loc, kind, import, branch, importer); + + // then, recursively check every single re-export on *our* side for conflicts: + for(auto rexp : import->reexports) + checkExportsRecursively(loc, kind, rexp, branch, importer, getExportInfo(import, rexp)); // then, also check that, for every one of *their* public imports: for(auto rexp : branch->reexports) { + auto exportInfo = getExportInfo(import, rexp); + // it doesn't trample with anything in our tree, - checkConflictingDefinitions(loc, kind, base, rexp); + checkConflictingDefinitions(loc, kind, base, rexp, importer, exportInfo); // and it doesn't conflict with anything in our imports. - checkConflictingDefinitions(loc, kind, import, rexp, base); + checkConflictingDefinitions(loc, kind, import, rexp, importer, exportInfo); + + // finally, also check that, for every one of *our* imports' re-exports, + for(auto rexp2 : import->reexports) + { + auto exportInfo = getExportInfo(import, rexp2); + + // check that *they* don't trample anything: + checkConflictingDefinitions(loc, kind, rexp2, branch, importer, exportInfo); + + // and neither does their reexport: + checkConflictingDefinitions(loc, kind, rexp2, rexp, importer, exportInfo); + } } } // no problem -- attach the trees base->imports.push_back(branch); - base->importMetadata[branch] = { loc, branch->name }; + base->importMetadata[branch] = { loc, base->name }; // merge the subtrees as well. for(const auto& [ name, tr ] : branch->subtrees) @@ -141,7 +212,7 @@ namespace sst DefinitionTree* typecheck(frontend::CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports, bool addPreludeDefinitions) { - auto tree = new StateTree(file.moduleName, file.name, 0); + auto tree = new StateTree(file.moduleName, nullptr); auto fs = new TypecheckState(tree); for(auto [ ithing, import ] : imports) @@ -178,7 +249,7 @@ namespace sst } else { - auto newinspt = util::pool(impas, file.name, curinspt); + auto newinspt = util::pool(impas, curinspt); curinspt->subtrees[impas] = newinspt; curinspt = newinspt; @@ -186,13 +257,17 @@ namespace sst } insertPoint = curinspt; + insertPoint->proxyOf = import->base; } iceAssert(insertPoint); mergeExternalTree(ithing.loc, "import", insertPoint, import->base); if(ithing.pubImport) + { insertPoint->reexports.push_back(import->base); + insertPoint->reexportMetadata[import->base] = { ithing.loc, file.moduleName }; + } fs->dtree->thingsImported.insert(ithing.name); fs->dtree->typeDefnMap.insert(import->typeDefnMap.begin(), import->typeDefnMap.end()); @@ -204,7 +279,7 @@ namespace sst } if(addPreludeDefinitions) - generatePreludeDefinitions(fs); + generatePreludeDefinitions(fs, file.name); // handle exception here: try { @@ -279,10 +354,10 @@ namespace sst return ret; } - static void generatePreludeDefinitions(TypecheckState* fs) + static void generatePreludeDefinitions(TypecheckState* fs, const std::string& filename) { auto loc = Location(); - loc.fileID = frontend::getFileIDFromFilename(fs->stree->topLevelFilename); + loc.fileID = frontend::getFileIDFromFilename(filename); auto strings = getOsStrings(); diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index 19159d84..db521f35 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -208,9 +208,14 @@ namespace sst return zfu::join(std::vector(scope.begin(), scope.end()), "::"); } - util::hash_map> StateTree::getAllDefinitions() + std::vector StateTree::getAllDefinitions() { - return this->definitions2; + std::vector ret; + for(const auto& [ n, ds ] : this->definitions2) + for(auto d : ds) + ret.push_back(d); + + return ret; } static void fetchDefinitionsFrom(const std::string& name, StateTree* tree, bool recursively, bool includePrivate, std::vector& out) @@ -222,12 +227,19 @@ namespace sst }); } + auto sameOrigin = [](const StateTree* a, const StateTree* b) -> bool { + auto p1 = a; while(p1->parent) p1 = p1->parent; + auto p2 = b; while(p2->parent) p2 = p2->parent; + + return p1 == p2; + }; + for(auto import : tree->imports) { if(recursively) { // only include private things if we're in the same file. - bool priv = tree->topLevelFilename == import->topLevelFilename; + bool priv = sameOrigin(tree, import); fetchDefinitionsFrom(name, import, /* recursively: */ false, /* includePrivate: */ priv, out); } @@ -254,21 +266,11 @@ namespace sst return { }; } - void StateTree::addDefinition(const std::string& sourceFile, const std::string& name, Defn* def, const TypeParamMap_t& gmaps) + void StateTree::addDefinition(const std::string& name, Defn* def, const TypeParamMap_t& gmaps) { this->definitions2[name].push_back(def); } - void StateTree::addDefinition(const std::string& _name, Defn* def, const TypeParamMap_t& gmaps) - { - this->addDefinition(this->topLevelFilename, _name, def, gmaps); - } - - - - - - std::string Scope::string() const { @@ -350,7 +352,7 @@ namespace sst } else { - auto newtree = util::pool(name, this->topLevelFilename, this, anonymous); + auto newtree = util::pool(name, this, anonymous); this->subtrees[name] = newtree; return newtree; } diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index fba82ae0..5b992e15 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -59,9 +59,13 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) error("unsupported LHS of using: '%s'", used->readableName); } + auto target = scopes.stree; + while(target->proxyOf) + target = target->proxyOf; + if(this->useAs == "_") { - sst::mergeExternalTree(this->loc, "using", fs->stree, scopes.stree); + sst::mergeExternalTree(this->loc, "using", fs->stree, target); } else { @@ -76,7 +80,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) } auto tree = fs->stree->findOrCreateSubtree(this->useAs); - sst::mergeExternalTree(this->loc, "using", tree, scopes.stree); + sst::mergeExternalTree(this->loc, "using", tree, target); } return TCResult::getDummy(); From fbf5e90e1bbaf59b14e5bc320e9574377488abe8 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 01:42:19 +0800 Subject: [PATCH 121/129] refactor stuff, and remove Identifier dependency from fir --- build/tmp/repro_1.flx | 5 +- build/tmp/repro_3.flx | 14 +- source/backend/llvm/translator.cpp | 8 +- source/codegen/alloc.cpp | 2 +- source/codegen/classes.cpp | 4 +- source/codegen/codegenstate.cpp | 24 +-- source/codegen/constructor.cpp | 2 +- source/codegen/directives.cpp | 5 +- source/codegen/enums.cpp | 2 +- source/codegen/function.cpp | 6 +- source/codegen/glue/misc.cpp | 7 +- source/codegen/literals.cpp | 6 +- source/codegen/misc.cpp | 2 +- source/codegen/variable.cpp | 2 +- source/fir/Function.cpp | 4 +- source/fir/GlobalValue.cpp | 2 +- source/fir/Module.cpp | 42 ++-- source/fir/Name.cpp | 290 ++++++++++++++++++++++++++ source/fir/Types/ClassType.cpp | 14 +- source/fir/Types/EnumType.cpp | 9 +- source/fir/Types/RawUnionType.cpp | 13 +- source/fir/Types/StructType.cpp | 14 +- source/fir/Types/TraitType.cpp | 11 +- source/fir/Types/UnionType.cpp | 13 +- source/fir/Value.cpp | 8 +- source/fir/interp/interpreter.cpp | 4 +- source/frontend/parser/type.cpp | 11 +- source/include/defs.h | 17 +- source/include/gluecode.h | 48 ++--- source/include/ir/constant.h | 2 +- source/include/ir/function.h | 4 +- source/include/ir/module.h | 38 ++-- source/include/ir/type.h | 134 +++++++++--- source/include/ir/value.h | 6 +- source/include/sst.h | 4 +- source/include/typecheck.h | 38 ++-- source/misc/identifier.cpp | 210 +------------------ source/repl/execute.cpp | 6 +- source/typecheck/classes.cpp | 6 +- source/typecheck/directives.cpp | 4 +- source/typecheck/dotop.cpp | 10 +- source/typecheck/enums.cpp | 6 +- source/typecheck/function.cpp | 11 +- source/typecheck/literals.cpp | 4 +- source/typecheck/misc.cpp | 2 +- source/typecheck/polymorph/driver.cpp | 2 +- source/typecheck/structs.cpp | 4 +- source/typecheck/toplevel.cpp | 38 ++-- source/typecheck/traits.cpp | 4 +- source/typecheck/type.cpp | 5 +- source/typecheck/typecheckstate.cpp | 23 +- source/typecheck/unions.cpp | 10 +- source/typecheck/using.cpp | 2 +- source/typecheck/variable.cpp | 4 +- 54 files changed, 665 insertions(+), 501 deletions(-) create mode 100644 source/fir/Name.cpp diff --git a/build/tmp/repro_1.flx b/build/tmp/repro_1.flx index 2cf0e699..1c687bfd 100644 --- a/build/tmp/repro_1.flx +++ b/build/tmp/repro_1.flx @@ -47,7 +47,7 @@ using repro_2 as _ import libc -import repro_3 as _ +import repro_3 // using libc as _ @@ -56,6 +56,7 @@ import repro_3 as _ // std::io::println("owo") // owo::println("owo") // uwu::owo::foozle() - printf("owo\n") + // printf("owo\n") + repro_3::foozle(10) } diff --git a/build/tmp/repro_3.flx b/build/tmp/repro_3.flx index 667bdfa5..5a4a6917 100644 --- a/build/tmp/repro_3.flx +++ b/build/tmp/repro_3.flx @@ -2,4 +2,16 @@ // Copyright (c) 2020, zhiayang // Licensed under the Apache License Version 2.0. -public import libc as _ +// public import libc as _ +import libc + +fn foozle(x: int, y: int) -> int +{ + return x * y +} + +public fn foozle(x: int) +{ + foozle(x, x + 2) + // libc::printf("asdf %d\n", foozle(x, x + 2)); +} diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index d9f346ce..640b8067 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -51,7 +51,7 @@ namespace backend { - static util::hash_map createdTypes; + static util::hash_map createdTypes; static std::map cachedConstants; @@ -208,7 +208,7 @@ namespace backend { llvm::Type* i8ptrtype = llvm::Type::getInt8PtrTy(gc); - auto id = util::obfuscateIdentifier("string", IdKind::Type); + auto id = fir::Name::obfuscate("string", fir::NameKind::Type); if(createdTypes.find(id) != createdTypes.end()) return createdTypes[id]; @@ -241,7 +241,7 @@ namespace backend } else if(type->isRangeType()) { - auto id = util::obfuscateIdentifier("range", IdKind::Type); + auto id = fir::Name::obfuscate("range", fir::NameKind::Type); if(createdTypes.find(id) != createdTypes.end()) return createdTypes[id]; @@ -262,7 +262,7 @@ namespace backend { llvm::Type* arrtype = llvm::ArrayType::get(llvm::Type::getInt8Ty(gc), BUILTIN_ANY_DATA_BYTECOUNT); - auto id = util::obfuscateIdentifier("any", IdKind::Type); + auto id = fir::Name::obfuscate("any", fir::NameKind::Type); if(createdTypes.find(id) != createdTypes.end()) return createdTypes[id]; diff --git a/source/codegen/alloc.cpp b/source/codegen/alloc.cpp index 801380ca..c56be3a6 100644 --- a/source/codegen/alloc.cpp +++ b/source/codegen/alloc.cpp @@ -9,7 +9,7 @@ static fir::Function* getCheckNegativeLengthFunction(cgn::CodegenState* cs) { - auto fname = util::obfuscateIdentifier("alloc_checkneg"); + auto fname = fir::Name::obfuscate("alloc_checkneg"); fir::Function* checkf = cs->module->getFunction(fname); if(!checkf) diff --git a/source/codegen/classes.cpp b/source/codegen/classes.cpp index f6e7737b..092662e5 100644 --- a/source/codegen/classes.cpp +++ b/source/codegen/classes.cpp @@ -66,7 +66,7 @@ CGResult sst::ClassDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) // make the inline initialiser { - fir::Function* func = cs->module->getOrCreateFunction(Identifier(this->id.mangled() + "_inline_init", IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fir::Name::obfuscate(clsty->encodedStr(), "_inline_init"), fir::FunctionType::get({ this->type->getMutablePointerTo() }, fir::Type::getVoid()), fir::LinkageType::Internal); @@ -114,7 +114,7 @@ CGResult sst::ClassDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) // this is the inline destructor { - fir::Function* func = cs->module->getOrCreateFunction(Identifier(this->id.mangled() + "_inline_deinit", IdKind::Name), + fir::Function* func = cs->module->getOrCreateFunction(fir::Name::obfuscate(clsty->encodedStr(), "_inline_deinit"), fir::FunctionType::get({ this->type->getMutablePointerTo() }, fir::Type::getVoid()), fir::LinkageType::Internal); diff --git a/source/codegen/codegenstate.cpp b/source/codegen/codegenstate.cpp index efbe0628..f09e0c8e 100644 --- a/source/codegen/codegenstate.cpp +++ b/source/codegen/codegenstate.cpp @@ -276,53 +276,53 @@ namespace cgn { if(name == ALLOCATE_MEMORY_FUNC) { - return this->module->getOrCreateFunction(Identifier(ALLOCATE_MEMORY_FUNC, IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of(ALLOCATE_MEMORY_FUNC), fir::FunctionType::get({ fir::Type::getNativeWord() }, fir::Type::getMutInt8Ptr()), fir::LinkageType::External); } else if(name == FREE_MEMORY_FUNC) { - return this->module->getOrCreateFunction(Identifier(FREE_MEMORY_FUNC, IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of(FREE_MEMORY_FUNC), fir::FunctionType::get({ fir::Type::getMutInt8Ptr() }, fir::Type::getVoid()), fir::LinkageType::External); } else if(name == REALLOCATE_MEMORY_FUNC) { - return this->module->getOrCreateFunction(Identifier(REALLOCATE_MEMORY_FUNC, IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of(REALLOCATE_MEMORY_FUNC), fir::FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getNativeWord() }, fir::Type::getMutInt8Ptr()), fir::LinkageType::External); } else if(name == CRT_FDOPEN) { - return this->module->getOrCreateFunction(Identifier(CRT_FDOPEN, IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of(CRT_FDOPEN), fir::FunctionType::get({ fir::Type::getInt32(), fir::Type::getInt8Ptr() }, fir::Type::getVoidPtr()), fir::LinkageType::External); } else if(name == "printf") { - return this->module->getOrCreateFunction(Identifier("printf", IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of("printf"), fir::FunctionType::getCVariadicFunc({ fir::Type::getInt8Ptr() }, fir::Type::getInt32()), fir::LinkageType::External); } else if(name == "abort") { - return this->module->getOrCreateFunction(Identifier("abort", IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of("abort"), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::External); } else if(name == "exit") { - return this->module->getOrCreateFunction(Identifier("exit", IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of("exit"), fir::FunctionType::get({ fir::Type::getInt32() }, fir::Type::getVoid()), fir::LinkageType::External); } else if(name == "strlen") { - return this->module->getOrCreateFunction(Identifier("strlen", IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of("strlen"), fir::FunctionType::get({ fir::Type::getInt8Ptr() }, fir::Type::getNativeWord()), fir::LinkageType::External); } else if(name == "fprintf") { - return this->module->getOrCreateFunction(Identifier("fprintf", IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of("fprintf"), fir::FunctionType::getCVariadicFunc({ fir::Type::getVoidPtr(), fir::Type::getInt8Ptr() }, fir::Type::getInt32()), fir::LinkageType::External); } else if(name == "fflush") { - return this->module->getOrCreateFunction(Identifier("fflush", IdKind::Name), + return this->module->getOrCreateFunction(fir::Name::of("fflush"), fir::FunctionType::get({ fir::Type::getVoidPtr() }, fir::Type::getInt32()), fir::LinkageType::External); } else @@ -348,7 +348,7 @@ namespace cgn { fir::Function* func = this->module->getOrCreateFunction( - util::obfuscateIdentifier(zpr::sprint("%s_piece_%d", strs::names::GLOBAL_INIT_FUNCTION, this->globalInitPieces.size())), + fir::Name::obfuscate(zpr::sprint("%s_piece_%d", strs::names::GLOBAL_INIT_FUNCTION, this->globalInitPieces.size())), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); auto b = this->irb.addNewBlockInFunction("b", func); @@ -386,7 +386,7 @@ namespace cgn else { this->finalisedGlobalInitFunction = this->module->getOrCreateFunction( - util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION), + fir::Name::obfuscate(strs::names::GLOBAL_INIT_FUNCTION), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); } diff --git a/source/codegen/constructor.cpp b/source/codegen/constructor.cpp index 534931b3..f79fabab 100644 --- a/source/codegen/constructor.cpp +++ b/source/codegen/constructor.cpp @@ -82,7 +82,7 @@ fir::Value* cgn::CodegenState::constructClassWithArguments(fir::ClassType* cls, // make a wrapper... - auto fname = util::obfuscateIdentifier("init_wrapper", constr->id.str()); + auto fname = fir::Name::obfuscate("init_wrapper", constr->id.str()); fir::Function* wrapper_func = this->module->getFunction(fname); if(!wrapper_func) diff --git a/source/codegen/directives.cpp b/source/codegen/directives.cpp index 6ba1a845..1cb7d574 100644 --- a/source/codegen/directives.cpp +++ b/source/codegen/directives.cpp @@ -13,7 +13,7 @@ #include "memorypool.h" fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, - const Identifier& fname, fir::interp::InterpState* is = 0) + const fir::Name& fname, fir::interp::InterpState* is = 0) { // what we do is to make a new function in IR, set the insertpoint to that, // then run codegen on the expression (so it generates inside), restore the insertpoint, @@ -116,7 +116,8 @@ CGResult sst::RunDirective::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->insideExpr) toExec = this->insideExpr; else toExec = this->block; - auto ret = magicallyRunExpressionAtCompileTime(cs, toExec, infer, util::obfuscateIdentifier("run_directive", runDirectiveId++)); + auto ret = magicallyRunExpressionAtCompileTime(cs, toExec, infer, fir::Name::obfuscate("run_directive", runDirectiveId++)); + return CGResult(ret); } diff --git a/source/codegen/enums.cpp b/source/codegen/enums.cpp index da051de7..36f14630 100644 --- a/source/codegen/enums.cpp +++ b/source/codegen/enums.cpp @@ -44,7 +44,7 @@ CGResult sst::EnumDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) // this is for the names... I guess? { auto array = fir::ConstantArray::get(fir::ArrayType::get(fir::Type::getCharSlice(false), names.size()), names); - et->setNameArray(cs->module->createGlobalVariable(Identifier("_FV_ENUM_NAME_ARR_" + this->id.str(), IdKind::Name), + et->setNameArray(cs->module->createGlobalVariable(fir::Name::obfuscate("_FV_ENUM_NAME_ARR_", this->id.str()), array->getType(), array, true, fir::LinkageType::Internal)); } diff --git a/source/codegen/function.cpp b/source/codegen/function.cpp index 857356e7..6f45b0bb 100644 --- a/source/codegen/function.cpp +++ b/source/codegen/function.cpp @@ -27,7 +27,7 @@ CGResult sst::FunctionDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) if(this->attrs.hasAny(attr::FN_ENTRYPOINT, attr::NO_MANGLE)) ident = Identifier(this->id.name, IdKind::Name); - auto fn = cs->module->getOrCreateFunction(ident, ft, + auto fn = cs->module->getOrCreateFunction(ident.convertToName(), ft, this->visibility == VisibilityLevel::Private ? fir::LinkageType::Internal : fir::LinkageType::External); // manually set the names, I guess @@ -117,14 +117,14 @@ CGResult sst::ForeignFuncDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) auto realId = Identifier(this->realName, IdKind::Name); - auto ef = cs->module->getFunction(realId); + auto ef = cs->module->getFunction(realId.convertToName()); if(ef && ef->getType() != ft) { error(this, "foreign function '%s' already defined elsewhere (with signature %s); overloading not possible", this->id.str(), ef->getType()); } - auto fn = cs->module->getOrCreateFunction(realId, ft, fir::LinkageType::External); + auto fn = cs->module->getOrCreateFunction(realId.convertToName(), ft, fir::LinkageType::External); if(this->isIntrinsic) fn->setIsIntrinsic(); diff --git a/source/codegen/glue/misc.cpp b/source/codegen/glue/misc.cpp index f698bdd2..87805d98 100644 --- a/source/codegen/glue/misc.cpp +++ b/source/codegen/glue/misc.cpp @@ -7,7 +7,6 @@ #include "gluecode.h" #include "frontend.h" - namespace cgn { namespace glue { @@ -191,11 +190,11 @@ namespace misc - using Idt = Identifier; + using Idt = fir::Name; Idt getOI(const std::string& name, fir::Type* t = 0) { - if(t) return util::obfuscateIdentifier(name, t->encodedStr()); - else return util::obfuscateIdentifier(name); + if(t) return fir::Name::obfuscate(name, t->encodedStr()); + else return fir::Name::obfuscate(name); } Idt getCompare_FName(fir::Type* t) { return getOI("compare", t); } diff --git a/source/codegen/literals.cpp b/source/codegen/literals.cpp index 32b7899f..b3b07d20 100644 --- a/source/codegen/literals.cpp +++ b/source/codegen/literals.cpp @@ -95,14 +95,16 @@ CGResult sst::LiteralArray::_codegen(cgn::CodegenState* cs, fir::Type* infer) // make a function specifically to initialise this thing static size_t _id = 0; + auto theId = _id++; + auto _aty = fir::ArrayType::get(elmty, this->values.size()); - auto array = cs->module->createGlobalVariable(Identifier("_FV_DAR_" + std::to_string(_id++), IdKind::Name), + auto array = cs->module->createGlobalVariable(fir::Name::obfuscate("_FV_DAR_", theId), _aty, fir::ConstantArray::getZeroValue(_aty), false, fir::LinkageType::Internal); { auto restore = cs->irb.getCurrentBlock(); - fir::Function* func = cs->module->getOrCreateFunction(util::obfuscateIdentifier("init_array", _id - 1), + fir::Function* func = cs->module->getOrCreateFunction(fir::Name::obfuscate("init_array", theId), fir::FunctionType::get({ }, fir::Type::getVoid()), fir::LinkageType::Internal); fir::IRBlock* entry = cs->irb.addNewBlockInFunction("entry", func); diff --git a/source/codegen/misc.cpp b/source/codegen/misc.cpp index 37d359bf..29f8b765 100644 --- a/source/codegen/misc.cpp +++ b/source/codegen/misc.cpp @@ -12,7 +12,7 @@ CGResult sst::TypeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) CGResult sst::ScopeExpr::_codegen(cgn::CodegenState* cs, fir::Type* infer) { - error(this, "failed to resolve scope '%s'", this->scope2.string()); + error(this, "failed to resolve scope '%s'", this->scope.string()); } CGResult sst::BareTypeDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) diff --git a/source/codegen/variable.cpp b/source/codegen/variable.cpp index 2409920f..9b384ea1 100644 --- a/source/codegen/variable.cpp +++ b/source/codegen/variable.cpp @@ -37,7 +37,7 @@ CGResult sst::VarDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer) { //* note: we declare it as not-immutable here to make it easier to set things, //* but otherwise we make it immutable again below after init. - auto glob = cs->module->createGlobalVariable(this->id, this->type, false, + auto glob = cs->module->createGlobalVariable(this->id.convertToName(), this->type, false, this->visibility == VisibilityLevel::Public ? fir::LinkageType::External : fir::LinkageType::Internal); auto rest = cs->enterGlobalInitFunction(glob); diff --git a/source/fir/Function.cpp b/source/fir/Function.cpp index e8d0b1ff..87c7ab77 100644 --- a/source/fir/Function.cpp +++ b/source/fir/Function.cpp @@ -57,7 +57,7 @@ namespace fir // function stuff - Function::Function(const Identifier& name, FunctionType* fnType, Module* module, LinkageType linkage) + Function::Function(const Name& name, FunctionType* fnType, Module* module, LinkageType linkage) : GlobalValue(module, fnType, linkage) { this->ident = name; @@ -176,7 +176,7 @@ namespace fir - Function* Function::create(const Identifier& name, fir::FunctionType* fnType, fir::Module* module, fir::LinkageType linkage) + Function* Function::create(const Name& name, fir::FunctionType* fnType, fir::Module* module, fir::LinkageType linkage) { return new Function(name, fnType, module, linkage); } diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index 907921a6..91917b88 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -27,7 +27,7 @@ namespace fir - GlobalVariable::GlobalVariable(const Identifier& name, Module* module, Type* type, bool immutable, LinkageType lt, ConstantValue* initValue) + GlobalVariable::GlobalVariable(const Name& name, Module* module, Type* type, bool immutable, LinkageType lt, ConstantValue* initValue) : GlobalValue(module, type, lt, !immutable) { this->ident = name; diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index 3035bf69..cfe6b706 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -26,7 +26,7 @@ namespace fir std::vector trymains = { "main", "_FF" + this->getModuleName() + "4main_FAv" }; for(const auto& m : trymains) { - entryfunc = this->getFunction(Identifier(m, IdKind::Name)); + entryfunc = this->getFunction(Name::of(m)); if(entryfunc) break; } @@ -51,7 +51,7 @@ namespace fir builder.setCurrentBlock(newentry); - auto gif = this->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION)); + auto gif = this->getFunction(Name::obfuscate(strs::names::GLOBAL_INIT_FUNCTION)); if(!gif) error("fir: failed to find global init function"); builder.Call(gif); @@ -69,7 +69,7 @@ namespace fir - GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) + GlobalVariable* Module::createGlobalVariable(const Name& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) { GlobalVariable* gv = new GlobalVariable(ident, this, type, isImmut, linkage, initVal); if(this->globals.find(ident) != this->globals.end()) @@ -79,17 +79,17 @@ namespace fir return gv; } - GlobalVariable* Module::createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage) + GlobalVariable* Module::createGlobalVariable(const Name& id, Type* type, bool isImmut, LinkageType linkage) { return this->createGlobalVariable(id, type, 0, isImmut, linkage); } - GlobalVariable* Module::declareGlobalVariable(const Identifier& id, Type* type, bool isImmut) + GlobalVariable* Module::declareGlobalVariable(const Name& id, Type* type, bool isImmut) { return this->createGlobalVariable(id, type, 0, isImmut, LinkageType::External); } - GlobalVariable* Module::tryGetGlobalVariable(const Identifier& id) + GlobalVariable* Module::tryGetGlobalVariable(const Name& id) { if(this->globals.find(id) == this->globals.end()) return 0; @@ -97,7 +97,7 @@ namespace fir return this->globals[id]; } - GlobalVariable* Module::getGlobalVariable(const Identifier& id) + GlobalVariable* Module::getGlobalVariable(const Name& id) { if(this->globals.find(id) == this->globals.end()) error("fir: no such global with name '%s'", id.str()); @@ -124,7 +124,7 @@ namespace fir // TODO: should we make the vtable immutable? auto table = ConstantArray::get(ArrayType::get(FunctionType::get({ }, Type::getVoid()), cls->virtualMethodCount), methods); - auto vtab = this->createGlobalVariable(util::obfuscateIdentifier("vtable", cls->getTypeName().mangled()), + auto vtab = this->createGlobalVariable(Name::obfuscate("vtable", cls->getTypeName().mangled()), table->getType(), table, true, LinkageType::External); this->vtables[cls] = { fmethods, vtab }; @@ -161,7 +161,7 @@ namespace fir - Type* Module::getNamedType(const Identifier& id) + Type* Module::getNamedType(const Name& id) { if(this->namedTypes.find(id) == this->namedTypes.end()) error("fir: no such type with name '%s'", id.str()); @@ -169,7 +169,7 @@ namespace fir return this->namedTypes[id]; } - void Module::addNamedType(const Identifier& id, Type* type) + void Module::addNamedType(const Name& id, Type* type) { if(this->namedTypes.find(id) != this->namedTypes.end()) error("fir: type '%s' exists already", id.str()); @@ -204,12 +204,12 @@ namespace fir } - Function* Module::declareFunction(const Identifier& id, FunctionType* ftype) + Function* Module::declareFunction(const Name& id, FunctionType* ftype) { return this->getOrCreateFunction(id, ftype, fir::LinkageType::External); } - Function* Module::getFunction(const Identifier& id) + Function* Module::getFunction(const Name& id) { if(this->functions.find(id) == this->functions.end()) return 0; @@ -217,7 +217,7 @@ namespace fir return this->functions[id]; } - std::vector Module::getFunctionsWithName(const Identifier& id) + std::vector Module::getFunctionsWithName(const Name& id) { // todo: *very* inefficient. @@ -231,7 +231,7 @@ namespace fir return ret; } - Function* Module::getOrCreateFunction(const Identifier& id, FunctionType* ftype, LinkageType linkage) + Function* Module::getOrCreateFunction(const Name& id, FunctionType* ftype, LinkageType linkage) { if(this->functions.find(id) != this->functions.end()) { @@ -282,7 +282,7 @@ namespace fir if(this->globalStrings.find(str) != this->globalStrings.end()) return this->globalStrings[str]; - GlobalVariable* gs = new GlobalVariable(Identifier("static_string" + std::to_string(stringId++), IdKind::Name), this, + GlobalVariable* gs = new GlobalVariable(Name::obfuscate("static_string", std::to_string(stringId++)), this, Type::getInt8Ptr(), true, LinkageType::Internal, 0); gs->setKind(Value::Kind::prvalue); @@ -395,25 +395,25 @@ namespace fir Function* Module::getIntrinsicFunction(const std::string& id) { - Identifier name; + auto name = Name::of(""); FunctionType* ft = 0; if(id == "memcpy") { - name = Identifier("memcpy", IdKind::Name); + name = Name::of("memcpy"); ft = FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getInt8Ptr(), fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getVoid()); } else if(id == "memmove") { - name = Identifier("memmove", IdKind::Name); + name = Name::of("memmove"); ft = FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getMutInt8Ptr(), fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getVoid()); } else if(id == "memset") { - name = Identifier("memset", IdKind::Name); + name = Name::of("memset"); ft = FunctionType::get({ fir::Type::getMutInt8Ptr(), fir::Type::getInt8(), fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getVoid()); @@ -423,7 +423,7 @@ namespace fir // note: memcmp isn't an actual llvm intrinsic, but we support it anyway // at llvm-translate-time, we make a function. - name = Identifier("memcmp", IdKind::Name); + name = Name::of("memcmp"); ft = FunctionType::get({ fir::Type::getInt8Ptr(), fir::Type::getInt8Ptr(), fir::Type::getNativeWord(), fir::Type::getBool() }, fir::Type::getInt32()); @@ -435,7 +435,7 @@ namespace fir // 1 -> 1 // 40 -> 64 - name = Identifier("roundup_pow2", IdKind::Name); + name = Name::of("roundup_pow2"); ft = FunctionType::get({ fir::Type::getNativeWord() }, fir::Type::getNativeWord()); } diff --git a/source/fir/Name.cpp b/source/fir/Name.cpp new file mode 100644 index 00000000..67267c07 --- /dev/null +++ b/source/fir/Name.cpp @@ -0,0 +1,290 @@ +// Name.cpp +// Copyright (c) 2020, zhiayang +// Licensed under the Apache License Version 2.0. + +#include "ir/type.h" + +namespace fir +{ + bool Name::operator== (const Name& other) const + { + return this->name == other.name + && this->scope == other.scope + && this->params == other.params + && this->retty == other.retty + && this->kind == other.kind; + } + + bool Name::operator!= (const Name& other) const + { + return !(*this == other); + } + + Name Name::of(std::string name) + { + return Name(NameKind::Name, name, { }, { }, nullptr); + } + + Name Name::of(std::string name, std::vector scope) + { + return Name(NameKind::Name, name, scope, { }, nullptr); + } + + Name Name::type(std::string name) + { + return Name(NameKind::Type, name, { }, { }, nullptr); + } + + Name Name::type(std::string name, std::vector scope) + { + return Name(NameKind::Type, name, scope, { }, nullptr); + } + + Name Name::function(std::string name, std::vector params, fir::Type* retty) + { + return Name(NameKind::Function, name, { }, params, retty); + } + + Name Name::function(std::string name, std::vector scope, std::vector params, fir::Type* retty) + { + return Name(NameKind::Function, name, scope, params, retty); + } + + + + + + static std::string mangleScopeOnly(const fir::Name& id) + { + bool first = true; + std::string ret; + for(const auto& s : id.scope) + { + ret += (!first ? std::to_string(s.length()) : "") + s; + first = false; + } + + return ret; + } + + static inline std::string lentypestr(const std::string& s) + { + return std::to_string(s.length()) + s; + } + + static std::string mangleScopeName(const fir::Name& id) + { + return mangleScopeOnly(id) + lentypestr(id.name); + } + + static std::string mangleType(fir::Type* t) + { + if(t->isPrimitiveType()) + { + return lentypestr(t->encodedStr()); + } + if(t->isBoolType()) + { + return lentypestr(t->encodedStr()); + } + else if(t->isArrayType()) + { + return "FA" + lentypestr(mangleType(t->getArrayElementType())) + std::to_string(t->toArrayType()->getArraySize()); + } + else if(t->isDynamicArrayType()) + { + return "DA" + lentypestr(mangleType(t->getArrayElementType())); + } + else if(t->isArraySliceType()) + { + return "SL" + lentypestr(mangleType(t->getArrayElementType())); + } + else if(t->isVoidType()) + { + return "v"; + } + else if(t->isFunctionType()) + { + std::string ret = "FN" + std::to_string(t->toFunctionType()->getArgumentCount()) + "FA"; + for(auto a : t->toFunctionType()->getArgumentTypes()) + { + ret += lentypestr(mangleType(a)); + } + + if(t->toFunctionType()->getArgumentTypes().empty()) + ret += "v"; + + return ret; + } + else if(t->isStructType()) + { + return lentypestr(mangleScopeName(t->toStructType()->getTypeName())); + } + else if(t->isClassType()) + { + return lentypestr(mangleScopeName(t->toClassType()->getTypeName())); + } + else if(t->isTupleType()) + { + std::string ret = "ST" + std::to_string(t->toTupleType()->getElementCount()) + "SM"; + for(auto m : t->toTupleType()->getElements()) + ret += lentypestr(mangleType(m)); + + return ret; + } + else if(t->isPointerType()) + { + return "PT" + lentypestr(mangleType(t->getPointerElementType())); + } + else if(t->isStringType()) + { + return "SR"; + } + else if(t->isCharType()) + { + return "CH"; + } + else if(t->isEnumType()) + { + return "EN" + lentypestr(mangleType(t->toEnumType()->getCaseType())) + lentypestr(mangleScopeName(t->toEnumType()->getTypeName())); + } + else if(t->isAnyType()) + { + return "AY"; + } + else + { + _error_and_exit("unsupported ir type??? ('%s')\n", t); + } + } + + static std::string mangleName(const Name& id, bool includeScope) + { + if(id.kind == NameKind::Name || id.kind == NameKind::Type) + { + std::string scp; + if(includeScope) + scp += mangleScopeOnly(id); + + if(includeScope && id.scope.size() > 0) + scp += std::to_string(id.name.length()); + + return scp + id.name; + } + else if(!includeScope) + { + if(id.kind == NameKind::Function) + { + std::string ret = id.name + "("; + + if(id.params.empty()) + { + ret += ")"; + } + else + { + for(auto t : id.params) + ret += t->str() + ","; + + ret = ret.substr(0, ret.length() - 1); + ret += ")"; + } + + return ret; + } + else + { + _error_and_exit("invalid\n"); + } + } + else + { + std::string ret = "_F"; + + if(id.kind == NameKind::Function) ret += "F"; + else if(id.kind == NameKind::Type) ret += "T"; + else ret += "U"; + + if(includeScope) + ret += mangleScopeOnly(id); + + ret += lentypestr(id.name); + + if(id.kind == NameKind::Function) + { + ret += "_FA"; + for(auto t : id.params) + ret += "_" + mangleType(t); + + if(id.params.empty()) + ret += "v"; + } + + return ret; + } + } + + std::string Name::str() const + { + std::string ret = zfu::join(this->scope, "."); + ret += this->name; + + if(this->kind == NameKind::Function) + { + ret += "("; + for(const auto& p : this->params) + ret += p->str() + ", "; + + if(this->params.size() > 0) + ret.pop_back(), ret.pop_back(); + + ret += ")"; + } + + return ret; + } + + std::string Name::mangled() const + { + return mangleName(*this, true); + } + + std::string Name::mangledWithoutScope() const + { + return mangleName(*this, false); + } + + std::string obfuscateName(const std::string& name) + { + return zpr::sprint("__#%s", name); + } + std::string obfuscateName(const std::string& name, size_t id) + { + return zpr::sprint("__#%s_%d", name, id); + } + std::string obfuscateName(const std::string& name, const std::string& extra) + { + return zpr::sprint("__#%s_%s", name, extra); + } + + Name Name::obfuscate(const std::string& name, NameKind kind) + { + return Name(kind, obfuscateName(name), { }, { }, nullptr); + } + Name Name::obfuscate(const std::string& name, size_t id, NameKind kind) + { + return Name(kind, obfuscateName(name, id), { }, { }, nullptr); + } + Name Name::obfuscate(const std::string& name, const std::string& extra, NameKind kind) + { + return Name(kind, obfuscateName(name, extra), { }, { }, nullptr); + } +} + +namespace zpr +{ + std::string print_formatter::print(const fir::Name& x, const format_args& args) + { + return x.str(); + } +} diff --git a/source/fir/Types/ClassType.cpp b/source/fir/Types/ClassType.cpp index 3eb4f666..54796e7b 100644 --- a/source/fir/Types/ClassType.cpp +++ b/source/fir/Types/ClassType.cpp @@ -9,18 +9,16 @@ namespace fir { // structs - ClassType::ClassType(const Identifier& name, const std::vector>& mems, const std::vector& methods, - const std::vector& inits) : Type(TypeKind::Class) + ClassType::ClassType(const Name& name, const std::vector>& mems, const std::vector& methods, + const std::vector& inits) : Type(TypeKind::Class), className(name) { - this->className = name; - this->setMembers(mems); this->setMethods(methods); this->setInitialiserFunctions(inits); } - static util::hash_map typeCache; - ClassType* ClassType::create(const Identifier& name, const std::vector>& members, + static util::hash_map typeCache; + ClassType* ClassType::create(const Name& name, const std::vector>& members, const std::vector& methods, const std::vector& inits) { if(auto it = typeCache.find(name); it != typeCache.end()) @@ -30,7 +28,7 @@ namespace fir return (typeCache[name] = new ClassType(name, members, methods, inits)); } - ClassType* ClassType::createWithoutBody(const Identifier& name) + ClassType* ClassType::createWithoutBody(const Name& name) { return ClassType::create(name, { }, { }, { }); } @@ -63,7 +61,7 @@ namespace fir // struct stuff - Identifier ClassType::getTypeName() + Name ClassType::getTypeName() { return this->className; } diff --git a/source/fir/Types/EnumType.cpp b/source/fir/Types/EnumType.cpp index 1154529c..aadeb271 100644 --- a/source/fir/Types/EnumType.cpp +++ b/source/fir/Types/EnumType.cpp @@ -10,13 +10,12 @@ namespace fir { - EnumType::EnumType(const Identifier& name, Type* ct) : Type(TypeKind::Enum) + EnumType::EnumType(const Name& name, Type* ct) : Type(TypeKind::Enum), typeName(name) { - this->typeName = name; this->caseType = ct; } - Identifier EnumType::getTypeName() + Name EnumType::getTypeName() { return this->typeName; } @@ -76,9 +75,9 @@ namespace fir - static util::hash_map typeCache; + static util::hash_map typeCache; - EnumType* EnumType::get(const Identifier& name, Type* caseType) + EnumType* EnumType::get(const Name& name, Type* caseType) { if(auto it = typeCache.find(name); it != typeCache.end()) error("enum with name '%s' already exists", name.str()); diff --git a/source/fir/Types/RawUnionType.cpp b/source/fir/Types/RawUnionType.cpp index 1305a9be..5e289b92 100644 --- a/source/fir/Types/RawUnionType.cpp +++ b/source/fir/Types/RawUnionType.cpp @@ -11,15 +11,14 @@ namespace fir { // structs - RawUnionType::RawUnionType(const Identifier& name, const util::hash_map& mems) - : Type(TypeKind::RawUnion) + RawUnionType::RawUnionType(const Name& name, const util::hash_map& mems) + : Type(TypeKind::RawUnion), unionName(name) { - this->unionName = name; this->setBody(mems); } - static util::hash_map typeCache; - RawUnionType* RawUnionType::create(const Identifier& name, const util::hash_map& mems) + static util::hash_map typeCache; + RawUnionType* RawUnionType::create(const Name& name, const util::hash_map& mems) { if(auto it = typeCache.find(name); it != typeCache.end()) error("union with name '%s' already exists", name.str()); @@ -28,7 +27,7 @@ namespace fir return (typeCache[name] = new RawUnionType(name, mems)); } - RawUnionType* RawUnionType::createWithoutBody(const Identifier& name) + RawUnionType* RawUnionType::createWithoutBody(const Name& name) { return RawUnionType::create(name, { }); } @@ -61,7 +60,7 @@ namespace fir // struct stuff - Identifier RawUnionType::getTypeName() + Name RawUnionType::getTypeName() { return this->unionName; } diff --git a/source/fir/Types/StructType.cpp b/source/fir/Types/StructType.cpp index 520ad047..a7ffb672 100644 --- a/source/fir/Types/StructType.cpp +++ b/source/fir/Types/StructType.cpp @@ -10,17 +10,15 @@ namespace fir { // structs - StructType::StructType(const Identifier& name, const std::vector>& mems, bool ispacked) - : Type(TypeKind::Struct) + StructType::StructType(const Name& name, const std::vector>& mems, bool ispacked) + : Type(TypeKind::Struct), structName(name) { - this->structName = name; this->isTypePacked = ispacked; - this->setBody(mems); } - static util::hash_map typeCache; - StructType* StructType::create(const Identifier& name, const std::vector>& members, bool packed) + static util::hash_map typeCache; + StructType* StructType::create(const Name& name, const std::vector>& members, bool packed) { if(auto it = typeCache.find(name); it != typeCache.end()) error("struct with name '%s' already exists", name.str()); @@ -29,7 +27,7 @@ namespace fir return (typeCache[name] = new StructType(name, members, packed)); } - StructType* StructType::createWithoutBody(const Identifier& name, bool isPacked) + StructType* StructType::createWithoutBody(const Name& name, bool isPacked) { return StructType::create(name, { }, isPacked); } @@ -62,7 +60,7 @@ namespace fir // struct stuff - Identifier StructType::getTypeName() + Name StructType::getTypeName() { return this->structName; } diff --git a/source/fir/Types/TraitType.cpp b/source/fir/Types/TraitType.cpp index 4be4de79..495536a3 100644 --- a/source/fir/Types/TraitType.cpp +++ b/source/fir/Types/TraitType.cpp @@ -11,15 +11,14 @@ namespace fir { // structs - TraitType::TraitType(const Identifier& name, const std::vector>& meths) - : Type(TypeKind::Trait) + TraitType::TraitType(const Name& name, const std::vector>& meths) + : Type(TypeKind::Trait), traitName(name) { - this->traitName = name; this->methods = meths; } - static util::hash_map typeCache; - TraitType* TraitType::create(const Identifier& name) + static util::hash_map typeCache; + TraitType* TraitType::create(const Name& name) { if(auto it = typeCache.find(name); it != typeCache.end()) error("trait with name '%s' already exists", name.str()); @@ -55,7 +54,7 @@ namespace fir // struct stuff - Identifier TraitType::getTypeName() + Name TraitType::getTypeName() { return this->traitName; } diff --git a/source/fir/Types/UnionType.cpp b/source/fir/Types/UnionType.cpp index e5bd4489..08ed8298 100644 --- a/source/fir/Types/UnionType.cpp +++ b/source/fir/Types/UnionType.cpp @@ -10,15 +10,14 @@ namespace fir { // structs - UnionType::UnionType(const Identifier& name, const util::hash_map>& mems) - : Type(TypeKind::Union) + UnionType::UnionType(const Name& name, const util::hash_map>& mems) + : Type(TypeKind::Union), unionName(name) { - this->unionName = name; this->setBody(mems); } - static util::hash_map typeCache; - UnionType* UnionType::create(const Identifier& name, const util::hash_map>& mems) + static util::hash_map typeCache; + UnionType* UnionType::create(const Name& name, const util::hash_map>& mems) { if(auto it = typeCache.find(name); it != typeCache.end()) error("union with name '%s' already exists", name.str()); @@ -27,7 +26,7 @@ namespace fir return (typeCache[name] = new UnionType(name, mems)); } - UnionType* UnionType::createWithoutBody(const Identifier& name) + UnionType* UnionType::createWithoutBody(const Name& name) { return UnionType::create(name, { }); } @@ -60,7 +59,7 @@ namespace fir // struct stuff - Identifier UnionType::getTypeName() + Name UnionType::getTypeName() { return this->unionName; } diff --git a/source/fir/Value.cpp b/source/fir/Value.cpp index ebe53f62..229a083a 100644 --- a/source/fir/Value.cpp +++ b/source/fir/Value.cpp @@ -8,7 +8,7 @@ namespace fir { static size_t vnames = 0; - Value::Value(Type* t, Kind k) : ident(), valueType(t), kind(k) + Value::Value(Type* t, Kind k) : ident(Name::of("")), valueType(t), kind(k) { this->id = vnames++; } @@ -25,17 +25,17 @@ namespace fir return this->ident.str() != ""; } - void Value::setName(const Identifier& name) + void Value::setName(const Name& name) { this->ident = name; } void Value::setName(const std::string& name) { - this->ident = Identifier(name, IdKind::Name); + this->ident = Name::of(name); } - const Identifier& Value::getName() + const Name& Value::getName() { return this->ident; } diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 4cd0ca89..1eb81bdc 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -481,7 +481,7 @@ namespace interp for(const auto& [ id, intr ] : this->module->_getIntrinsicFunctions()) { - auto fname = Identifier("__interp_intrinsic_" + id.str(), IdKind::Name); + auto fname = Name::obfuscate("__interp_intrinsic_", id.str()); auto fn = this->module->getOrCreateFunction(fname, intr->getType(), fir::LinkageType::ExternalWeak); // interp::compileFunction already maps the newly compiled interp::Function, but since we created a @@ -520,7 +520,7 @@ namespace interp // printf("module:\n%s\n", this->module->print().c_str()); // truth be told it'll be more efficient to only get the function after checking runGlobalInit, but... meh. - if(auto gif = this->module->getFunction(util::obfuscateIdentifier(strs::names::GLOBAL_INIT_FUNCTION)); + if(auto gif = this->module->getFunction(Name::obfuscate(strs::names::GLOBAL_INIT_FUNCTION)); runGlobalInit && gif) { auto cgif = this->compileFunction(gif); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index 77d125e6..112328ce 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -10,6 +10,13 @@ using namespace ast; using namespace lexer; +// defined in fir/name.cpp +namespace fir +{ + std::string obfuscateName(const std::string& name, size_t id); +} + + namespace parser { using TT = lexer::TokenType; @@ -175,7 +182,7 @@ namespace parser StructDefn* defn = util::pool(st.loc()); if(nameless) { - defn->name = util::obfuscateName("anon_struct", anon_counter++); + defn->name = fir::obfuscateName("anon_struct", anon_counter++); } else { @@ -313,7 +320,7 @@ namespace parser UnionDefn* defn = util::pool(st.loc()); if(nameless) { - defn->name = util::obfuscateName("anon_union", anon_counter++); + defn->name = fir::obfuscateName("anon_union", anon_counter++); } else { diff --git a/source/include/defs.h b/source/include/defs.h index 0d998344..3210ba89 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -14,7 +14,7 @@ struct Identifier; enum class VisibilityLevel; -namespace fir { struct Type; } +namespace fir { struct Type; struct Name; } namespace pts { struct Type; } @@ -167,15 +167,15 @@ struct Identifier Identifier(const std::string& n, IdKind k) : name(n), kind(k) { } std::string name; - sst::Scope scope2; - std::vector scope_; + sst::Scope scope; std::vector params; + fir::Type* returnType = nullptr; IdKind kind; std::string str() const; - std::string mangled() const; - std::string mangledName() const; + std::string mangled___() const; + fir::Name convertToName() const; bool operator == (const Identifier& other) const; bool operator != (const Identifier& other) const; @@ -649,13 +649,6 @@ namespace util { std::string typeParamMapToString(const std::string& name, const TypeParamMap_t& map); - std::string obfuscateName(const std::string& name); - std::string obfuscateName(const std::string& name, size_t id); - std::string obfuscateName(const std::string& name, const std::string& extra); - Identifier obfuscateIdentifier(const std::string& name, IdKind kind = IdKind::Name); - Identifier obfuscateIdentifier(const std::string& name, size_t id, IdKind kind = IdKind::Name); - Identifier obfuscateIdentifier(const std::string& name, const std::string& extra, IdKind kind = IdKind::Name); - template std::string listToEnglish(const std::vector& list, bool quote = true) { diff --git a/source/include/gluecode.h b/source/include/gluecode.h index 46e235a4..e2236881 100644 --- a/source/include/gluecode.h +++ b/source/include/gluecode.h @@ -122,37 +122,37 @@ namespace cgn fir::Function* getMallocWrapperFunction(CodegenState* cs); fir::Function* getRangeSanityCheckFunction(CodegenState* cs); - Identifier getCompare_FName(fir::Type* t); - Identifier getSetElements_FName(fir::Type* t); - Identifier getSetElementsDefault_FName(fir::Type* t); - Identifier getCallClassConstructor_FName(fir::Type* t); + fir::Name getCompare_FName(fir::Type* t); + fir::Name getSetElements_FName(fir::Type* t); + fir::Name getSetElementsDefault_FName(fir::Type* t); + fir::Name getCallClassConstructor_FName(fir::Type* t); - Identifier getClone_FName(fir::Type* t); - Identifier getAppend_FName(fir::Type* t); - Identifier getPopBack_FName(fir::Type* t); - Identifier getMakeFromTwo_FName(fir::Type* t); - Identifier getMakeFromOne_FName(fir::Type* t); - Identifier getReserveExtra_FName(fir::Type* t); - Identifier getAppendElement_FName(fir::Type* t); - Identifier getReserveEnough_FName(fir::Type* t); - Identifier getRecursiveRefcount_FName(fir::Type* t, bool incr); + fir::Name getClone_FName(fir::Type* t); + fir::Name getAppend_FName(fir::Type* t); + fir::Name getPopBack_FName(fir::Type* t); + fir::Name getMakeFromTwo_FName(fir::Type* t); + fir::Name getMakeFromOne_FName(fir::Type* t); + fir::Name getReserveExtra_FName(fir::Type* t); + fir::Name getAppendElement_FName(fir::Type* t); + fir::Name getReserveEnough_FName(fir::Type* t); + fir::Name getRecursiveRefcount_FName(fir::Type* t, bool incr); - Identifier getIncrRefcount_FName(fir::Type* t); - Identifier getDecrRefcount_FName(fir::Type* t); + fir::Name getIncrRefcount_FName(fir::Type* t); + fir::Name getDecrRefcount_FName(fir::Type* t); - Identifier getLoopIncrRefcount_FName(fir::Type* t); - Identifier getLoopDecrRefcount_FName(fir::Type* t); + fir::Name getLoopIncrRefcount_FName(fir::Type* t); + fir::Name getLoopDecrRefcount_FName(fir::Type* t); - Identifier getCreateAnyOf_FName(fir::Type* t); - Identifier getGetValueFromAny_FName(fir::Type* t); + fir::Name getCreateAnyOf_FName(fir::Type* t); + fir::Name getGetValueFromAny_FName(fir::Type* t); - Identifier getBoundsCheck_FName(); - Identifier getDecompBoundsCheck_FName(); + fir::Name getBoundsCheck_FName(); + fir::Name getDecompBoundsCheck_FName(); - Identifier getMallocWrapper_FName(); - Identifier getRangeSanityCheck_FName(); + fir::Name getMallocWrapper_FName(); + fir::Name getRangeSanityCheck_FName(); - Identifier getUtf8Length_FName(); + fir::Name getUtf8Length_FName(); } } } diff --git a/source/include/ir/constant.h b/source/include/ir/constant.h index 1f33a26a..9a1238bd 100644 --- a/source/include/ir/constant.h +++ b/source/include/ir/constant.h @@ -299,7 +299,7 @@ namespace fir { friend struct Module; - GlobalVariable(const Identifier& idt, Module* module, Type* type, bool immutable, LinkageType linkage, ConstantValue* initValue); + GlobalVariable(const Name& idt, Module* module, Type* type, bool immutable, LinkageType linkage, ConstantValue* initValue); void setInitialValue(ConstantValue* constVal); ConstantValue* getInitialValue(); diff --git a/source/include/ir/function.h b/source/include/ir/function.h index 5278f6c9..e385ef21 100644 --- a/source/include/ir/function.h +++ b/source/include/ir/function.h @@ -78,12 +78,12 @@ namespace fir // overridden stuff virtual FunctionType* getType() override; // override because better (more specific) return type. - static Function* create(const Identifier& name, FunctionType* fnType, Module* module, LinkageType linkage); + static Function* create(const Name& name, FunctionType* fnType, Module* module, LinkageType linkage); // fields protected: - Function(const Identifier& name, FunctionType* fnType, Module* module, LinkageType linkage); + Function(const Name& name, FunctionType* fnType, Module* module, LinkageType linkage); std::vector fnArguments; std::vector blocks; std::vector stackAllocs; diff --git a/source/include/ir/module.h b/source/include/ir/module.h index 10d502fa..3cd45960 100644 --- a/source/include/ir/module.h +++ b/source/include/ir/module.h @@ -21,11 +21,11 @@ namespace fir Module(const std::string& nm); - GlobalVariable* createGlobalVariable(const Identifier& id, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage); - GlobalVariable* createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage); - GlobalVariable* declareGlobalVariable(const Identifier& id, Type* type, bool isImmut); - GlobalVariable* tryGetGlobalVariable(const Identifier& id); - GlobalVariable* getGlobalVariable(const Identifier& id); + GlobalVariable* createGlobalVariable(const Name& id, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage); + GlobalVariable* createGlobalVariable(const Name& id, Type* type, bool isImmut, LinkageType linkage); + GlobalVariable* declareGlobalVariable(const Name& id, Type* type, bool isImmut); + GlobalVariable* tryGetGlobalVariable(const Name& id); + GlobalVariable* getGlobalVariable(const Name& id); GlobalVariable* getOrCreateVirtualTableForClass(ClassType* cls); @@ -36,18 +36,18 @@ namespace fir std::vector getNamedTypes(); // note: only looks at the name + scope, excludes the parameter list. - std::vector getFunctionsWithName(const Identifier& id); + std::vector getFunctionsWithName(const Name& id); Function* getIntrinsicFunction(const std::string& id); - Type* getNamedType(const Identifier& name); - void addNamedType(const Identifier& name, Type* type); + Type* getNamedType(const Name& name); + void addNamedType(const Name& name, Type* type); void addFunction(Function* func); void removeFunction(Function* func); - Function* declareFunction(const Identifier& id, FunctionType* ftype); - Function* getFunction(const Identifier& id); - Function* getOrCreateFunction(const Identifier& id, FunctionType* ftype, LinkageType linkage); + Function* declareFunction(const Name& id, FunctionType* ftype); + Function* getFunction(const Name& id); + Function* getOrCreateFunction(const Name& id, FunctionType* ftype, LinkageType linkage); std::string getModuleName(); void setModuleName(const std::string& name); @@ -61,11 +61,11 @@ namespace fir void finaliseGlobalConstructors(); const util::hash_map, GlobalVariable*>>& _getVtables() { return this->vtables; } - const util::hash_map& _getIntrinsicFunctions() { return this->intrinsicFunctions; } + const util::hash_map& _getIntrinsicFunctions() { return this->intrinsicFunctions; } const util::hash_map& _getGlobalStrings() { return this->globalStrings; } - const util::hash_map& _getGlobals() { return this->globals; } - const util::hash_map& _getFunctions() { return this->functions; } - const util::hash_map& _getNamedTypes() { return this->namedTypes; } + const util::hash_map& _getGlobals() { return this->globals; } + const util::hash_map& _getFunctions() { return this->functions; } + const util::hash_map& _getNamedTypes() { return this->namedTypes; } private: @@ -73,11 +73,11 @@ namespace fir util::hash_map, GlobalVariable*>> vtables; util::hash_map globalStrings; - util::hash_map globals; - util::hash_map functions; - util::hash_map namedTypes; + util::hash_map globals; + util::hash_map functions; + util::hash_map namedTypes; - util::hash_map intrinsicFunctions; + util::hash_map intrinsicFunctions; Function* entryFunction = 0; }; diff --git a/source/include/ir/type.h b/source/include/ir/type.h index cb21747f..2dc633b7 100644 --- a/source/include/ir/type.h +++ b/source/include/ir/type.h @@ -7,6 +7,84 @@ #include "defs.h" #include "precompile.h" +namespace fir +{ + enum class NameKind + { + Name, + Type, + Function + }; + + struct Type; + + struct Name + { + NameKind kind; + + std::string name; + std::vector scope; + std::vector params; + fir::Type* retty; + + std::string str() const; + std::string mangled() const; + std::string mangledWithoutScope() const; + + bool operator== (const Name& other) const; + bool operator!= (const Name& other) const; + + static Name of(std::string name); + static Name of(std::string name, std::vector scope); + + static Name type(std::string name); + static Name type(std::string name, std::vector scope); + + static Name function(std::string name, std::vector params, fir::Type* retty); + static Name function(std::string name, std::vector scope, std::vector params, fir::Type* retty); + + static Name obfuscate(const std::string& name, NameKind kind = NameKind::Name); + static Name obfuscate(const std::string& name, size_t id, NameKind kind = NameKind::Name); + static Name obfuscate(const std::string& name, const std::string& extra, NameKind kind = NameKind::Name); + + private: + Name(NameKind kind, std::string name, std::vector scope, std::vector params, fir::Type* retty) + : kind(kind), name(std::move(name)), scope(std::move(scope)), params(std::move(params)), retty(retty) { } + }; + + std::string obfuscateName(const std::string& name); + std::string obfuscateName(const std::string& name, size_t id); + std::string obfuscateName(const std::string& name, const std::string& extra); +} + +template <> +struct zpr::print_formatter +{ + std::string print(const fir::Name& x, const format_args& args); +}; + +namespace std +{ + template<> + struct hash + { + std::size_t operator()(const fir::Name& k) const + { + using std::size_t; + using std::hash; + using std::string; + + // Compute individual hash values for first, + // second and third and combine them using XOR + // and bit shifting: + + // return ((hash()(k.name) ^ (hash>()(k.scope) << 1)) >> 1) ^ (hash()(k.third) << 1); + return hash()(k.str()); + } + }; +} + + namespace fir { // NOTE: i don't really want to deal with inheritance stuff right now, @@ -557,7 +635,7 @@ namespace fir virtual bool isTypeEqual(Type* other) override; virtual Type* substitutePlaceholders(const util::hash_map& subst) override; - Identifier getTypeName(); + Name getTypeName(); size_t getVariantCount(); size_t getIdOfVariant(const std::string& name); @@ -571,15 +649,15 @@ namespace fir virtual ~UnionType() override { } protected: - UnionType(const Identifier& id, const util::hash_map>& variants); + UnionType(const Name& id, const util::hash_map>& variants); - Identifier unionName; + Name unionName; util::hash_map indexMap; util::hash_map variants; public: - static UnionType* create(const Identifier& id, const util::hash_map>& variants); - static UnionType* createWithoutBody(const Identifier& id); + static UnionType* create(const Name& id, const util::hash_map>& variants); + static UnionType* createWithoutBody(const Name& id); }; @@ -618,7 +696,7 @@ namespace fir { friend struct Type; - Identifier getTypeName(); + Name getTypeName(); size_t getVariantCount(); bool hasVariant(const std::string& name); @@ -636,14 +714,14 @@ namespace fir virtual ~RawUnionType() override { } protected: - RawUnionType(const Identifier& id, const util::hash_map& variants); + RawUnionType(const Name& id, const util::hash_map& variants); - Identifier unionName; + Name unionName; util::hash_map variants; public: - static RawUnionType* create(const Identifier& id, const util::hash_map& variants); - static RawUnionType* createWithoutBody(const Identifier& id); + static RawUnionType* create(const Name& id, const util::hash_map& variants); + static RawUnionType* createWithoutBody(const Name& id); }; @@ -653,7 +731,7 @@ namespace fir friend struct Type; // methods - Identifier getTypeName(); + Name getTypeName(); size_t getMethodCount(); const std::vector>& getMethods(); void setMethods(const std::vector>& m); @@ -666,15 +744,15 @@ namespace fir // protected constructor virtual ~TraitType() override { } protected: - TraitType(const Identifier& name, const std::vector>& meths); + TraitType(const Name& name, const std::vector>& meths); // fields - Identifier traitName; + Name traitName; std::vector> methods; // static funcs public: - static TraitType* create(const Identifier& name); + static TraitType* create(const Name& name); }; @@ -684,7 +762,7 @@ namespace fir friend struct Type; // methods - Identifier getTypeName(); + Name getTypeName(); size_t getElementCount(); Type* getElementN(size_t n); Type* getElement(const std::string& name); @@ -707,11 +785,11 @@ namespace fir // protected constructor virtual ~StructType() override { } protected: - StructType(const Identifier& name, const std::vector>& mems, bool ispacked); + StructType(const Name& name, const std::vector>& mems, bool ispacked); // fields (protected) bool isTypePacked; - Identifier structName; + Name structName; std::vector typeList; std::vector implTraits; util::hash_map indexMap; @@ -719,8 +797,8 @@ namespace fir // static funcs public: - static StructType* createWithoutBody(const Identifier& name, bool isPacked = false); - static StructType* create(const Identifier& name, const std::vector>& members, + static StructType* createWithoutBody(const Name& name, bool isPacked = false); + static StructType* create(const Name& name, const std::vector>& members, bool isPacked = false); }; @@ -734,7 +812,7 @@ namespace fir friend struct Module; // methods - Identifier getTypeName(); + Name getTypeName(); size_t getElementCount(); // Type* getElementN(size_t n); Type* getElement(const std::string& name); @@ -792,12 +870,12 @@ namespace fir // protected constructor virtual ~ClassType() override { } protected: - ClassType(const Identifier& name, const std::vector>& mems, const std::vector& methods, + ClassType(const Name& name, const std::vector>& mems, const std::vector& methods, const std::vector& inits); // fields (protected) - Identifier className; + Name className; std::vector typeList; std::vector nameList; std::vector methodList; @@ -832,8 +910,8 @@ namespace fir // static funcs public: - static ClassType* createWithoutBody(const Identifier& name); - static ClassType* create(const Identifier& name, const std::vector>& members, + static ClassType* createWithoutBody(const Name& name); + static ClassType* create(const Name& name, const std::vector>& members, const std::vector& methods, const std::vector& inits); }; @@ -843,7 +921,7 @@ namespace fir friend struct Type; Type* getCaseType(); - Identifier getTypeName(); + Name getTypeName(); ConstantValue* getNameArray(); ConstantValue* getCaseArray(); @@ -860,17 +938,17 @@ namespace fir // protected constructor virtual ~EnumType() override { } protected: - EnumType(const Identifier& name, Type* ty); + EnumType(const Name& name, Type* ty); Type* caseType; - Identifier typeName; + Name typeName; ConstantValue* runtimeNameArray = 0; ConstantValue* runtimeCasesArray = 0; // static funcs public: - static EnumType* get(const Identifier& name, Type* caseType); + static EnumType* get(const Name& name, Type* caseType); }; diff --git a/source/include/ir/value.h b/source/include/ir/value.h index ed26d8bb..32a3a04f 100644 --- a/source/include/ir/value.h +++ b/source/include/ir/value.h @@ -59,9 +59,9 @@ namespace fir void makeConst() { this->isconst = true; } // methods - void setName(const Identifier& idt); + void setName(const Name& idt); void setName(const std::string& s); - const Identifier& getName(); + const Name& getName(); bool hasName(); static size_t getCurrentValueId(); @@ -75,7 +75,7 @@ namespace fir // fields - Identifier ident; + Name ident; Type* valueType; Kind kind; bool isconst = false; diff --git a/source/include/sst.h b/source/include/sst.h index 38ea5598..27100488 100644 --- a/source/include/sst.h +++ b/source/include/sst.h @@ -401,14 +401,14 @@ namespace sst struct ScopeExpr : Expr { - ScopeExpr(const Location& l, fir::Type* t, const Scope& scope) : Expr(l, t), scope2(scope) + ScopeExpr(const Location& l, fir::Type* t, const Scope& scope) : Expr(l, t), scope(scope) { this->readableName = ""; } ~ScopeExpr() { } virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override; - Scope scope2; + Scope scope; }; struct FieldDotOp : Expr diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 9858d53d..4d73b5d1 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -51,6 +51,7 @@ namespace sst StateTree(const std::string& nm, StateTree* p, bool anon = false) : name(nm), parent(p), isAnonymous(anon) { } std::string name; + std::string moduleName; StateTree* parent = 0; @@ -67,24 +68,25 @@ namespace sst util::hash_map subtrees; util::hash_map> unresolvedGenericDefs; - util::hash_map>, sst::Defn*> resolvedGenericDefs; - util::hash_map> definitions2; + util::hash_map> definitions; std::vector exports; std::vector imports; std::vector reexports; + // when importing *not* `as _`, the module goes into a new StateTree with the same name, and that new tree + // is the one that imports the target module. (this is because the 'imports' list does not introduce a "new layer") + // so, we must point the newly created module to the target module somehow. StateTree* proxyOf = nullptr; // this is a mapping from every StateTree in `imports` to the location of the `import` or `using` statement. // it only used for error-reporting, so it is stored out-of-line so the usage of `this->imports` is not more - // cumbersome than it needs to be. the string in the pair stores the name of the module *IMPORTING* the module, - // not the name of the module being imported (that can be gotten from the StateTree). - std::map> importMetadata; + // cumbersome than it needs to be. + std::map importMetadata; // the same thing, but for reexports (ie. public imports). - std::map> reexportMetadata; + std::map reexportMetadata; // what's there to explain? a simple map of operators to their functions. we use @@ -131,10 +133,6 @@ namespace sst util::hash_map& typeDefnMap; - std::vector locationStack; - - void pushLoc(const Location& l); - void pushLoc(ast::Stmt* stmt); std::vector bodyStack; std::vector currentFunctionStack; @@ -181,17 +179,19 @@ namespace sst void leaveDeferBlock(); bool isInDeferBlock(); - Location loc(); + + std::vector locationStack; + void pushLoc(const Location& l); + void pushLoc(ast::Stmt* stmt); Location popLoc(); + Location loc(); + void pushAnonymousTree(); void pushTree(const std::string& name, bool createAnonymously = false); StateTree* popTree(); - void pushAnonymousTree(); - std::string serialiseCurrentScope(); - - Scope getCurrentScope2(); + Scope scope(); std::vector teleportationStack; void teleportInto(const Scope& scope); @@ -205,9 +205,7 @@ namespace sst fir::Type* getBinaryOpResultType(fir::Type* a, fir::Type* b, const std::string& op, sst::FunctionDefn** overloadFn = 0); - // things that i might want to make non-methods someday fir::Type* convertParserTypeToFIR(pts::Type* pt, bool allowFailure = false); - fir::Type* inferCorrectTypeForLiteral(fir::ConstantNumberType* lit); fir::Type* checkIsBuiltinConstructorCall(const std::string& name, const std::vector& arguments); @@ -219,15 +217,15 @@ namespace sst DecompMapping typecheckDecompositions(const DecompMapping& bind, fir::Type* rhs, bool immut, bool allowref); }; - + fir::Type* inferCorrectTypeForLiteral(fir::ConstantNumberType* lit); bool isDuplicateOverload(const std::vector& a, const std::vector& b); int getOverloadDistance(const std::vector& a, const std::vector& b); + void mergeExternalTree(const Location& importer, const char* kind, sst::StateTree* base, sst::StateTree* branch); + DefinitionTree* typecheck(frontend::CollectorState* cs, const parser::ParsedFile& file, const std::vector>& imports, bool addPreludeDefinitions); - - void mergeExternalTree(const Location& importer, const char* kind, sst::StateTree* base, sst::StateTree* branch); } diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 7b14590b..ef737302 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -80,30 +80,6 @@ void PolyArgMapping_t::add(size_t idx, pts::Type* t) -std::string util::obfuscateName(const std::string& name) -{ - return strprintf("__#%s", name); -} -std::string util::obfuscateName(const std::string& name, size_t id) -{ - return strprintf("__#%s_%d", name, id); -} -std::string util::obfuscateName(const std::string& name, const std::string& extra) -{ - return strprintf("__#%s_%s", name, extra); -} -Identifier util::obfuscateIdentifier(const std::string& name, IdKind kind) -{ - return Identifier(obfuscateName(name), kind); -} -Identifier util::obfuscateIdentifier(const std::string& name, size_t id, IdKind kind) -{ - return Identifier(obfuscateName(name, id), kind); -} -Identifier util::obfuscateIdentifier(const std::string& name, const std::string& extra, IdKind kind) -{ - return Identifier(obfuscateName(name, extra), kind); -} @@ -127,7 +103,7 @@ bool Identifier::operator != (const Identifier& other) const std::string Identifier::str() const { std::string ret; - for(const auto& s : this->scope2.components()) + for(const auto& s : this->scope.components()) ret += s + "."; ret += this->name; @@ -147,187 +123,15 @@ std::string Identifier::str() const return ret; } - - -static std::string mangleScopeOnly(const Identifier& id) +fir::Name Identifier::convertToName() const { - bool first = true; - std::string ret; - for(const auto& s : id.scope2.components()) + switch(this->kind) { - ret += (!first ? std::to_string(s.length()) : "") + s; - first = false; + case IdKind::Name: return fir::Name::of(this->name, this->scope.components()); + case IdKind::Type: return fir::Name::type(this->name, this->scope.components()); + case IdKind::Function: return fir::Name::function(this->name, this->scope.components(), this->params, this->returnType); + default: iceAssert(0 && "invalid identifier"); } - - return ret; -} - -static inline std::string lentypestr(const std::string& s) -{ - return std::to_string(s.length()) + s; -} - -static std::string mangleScopeName(const Identifier& id) -{ - return mangleScopeOnly(id) + lentypestr(id.name); -} - -static std::string mangleType(fir::Type* t) -{ - if(t->isPrimitiveType()) - { - return lentypestr(t->encodedStr()); - } - if(t->isBoolType()) - { - return lentypestr(t->encodedStr()); - } - else if(t->isArrayType()) - { - return "FA" + lentypestr(mangleType(t->getArrayElementType())) + std::to_string(t->toArrayType()->getArraySize()); - } - else if(t->isDynamicArrayType()) - { - return "DA" + lentypestr(mangleType(t->getArrayElementType())); - } - else if(t->isArraySliceType()) - { - return "SL" + lentypestr(mangleType(t->getArrayElementType())); - } - else if(t->isVoidType()) - { - return "v"; - } - else if(t->isFunctionType()) - { - std::string ret = "FN" + std::to_string(t->toFunctionType()->getArgumentCount()) + "FA"; - for(auto a : t->toFunctionType()->getArgumentTypes()) - { - ret += lentypestr(mangleType(a)); - } - - if(t->toFunctionType()->getArgumentTypes().empty()) - ret += "v"; - - return ret; - } - else if(t->isStructType()) - { - return lentypestr(mangleScopeName(t->toStructType()->getTypeName())); - } - else if(t->isClassType()) - { - return lentypestr(mangleScopeName(t->toClassType()->getTypeName())); - } - else if(t->isTupleType()) - { - std::string ret = "ST" + std::to_string(t->toTupleType()->getElementCount()) + "SM"; - for(auto m : t->toTupleType()->getElements()) - ret += lentypestr(mangleType(m)); - - return ret; - } - else if(t->isPointerType()) - { - return "PT" + lentypestr(mangleType(t->getPointerElementType())); - } - else if(t->isStringType()) - { - return "SR"; - } - else if(t->isCharType()) - { - return "CH"; - } - else if(t->isEnumType()) - { - return "EN" + lentypestr(mangleType(t->toEnumType()->getCaseType())) + lentypestr(mangleScopeName(t->toEnumType()->getTypeName())); - } - else if(t->isAnyType()) - { - return "AY"; - } - else - { - _error_and_exit("unsupported ir type??? ('%s')\n", t); - } -} - -static std::string _doMangle(const Identifier& id, bool includeScope) -{ - if(id.kind == IdKind::Name || id.kind == IdKind::Type) - { - std::string scp; - if(includeScope) - scp += mangleScopeOnly(id); - - if(includeScope && id.scope2.prev != nullptr) - scp += std::to_string(id.name.length()); - - return scp + id.name; - } - else if(!includeScope) - { - if(id.kind == IdKind::Function) - { - std::string ret = id.name + "("; - - if(id.params.empty()) - { - ret += ")"; - } - else - { - for(auto t : id.params) - ret += t->str() + ","; - - ret = ret.substr(0, ret.length() - 1); - ret += ")"; - } - - return ret; - } - else - { - _error_and_exit("invalid\n"); - } - } - else - { - std::string ret = "_F"; - - if(id.kind == IdKind::Function) ret += "F"; - else if(id.kind == IdKind::Type) ret += "T"; - else ret += "U"; - - if(includeScope) - ret += mangleScopeOnly(id); - - ret += lentypestr(id.name); - - if(id.kind == IdKind::Function) - { - ret += "_FA"; - for(auto t : id.params) - ret += "_" + mangleType(t); - - if(id.params.empty()) - ret += "v"; - } - - return ret; - } -} - - -std::string Identifier::mangled() const -{ - return _doMangle(*this, true); -} - -std::string Identifier::mangledName() const -{ - return _doMangle(*this, false); } std::string Location::toString() const diff --git a/source/repl/execute.cpp b/source/repl/execute.cpp index 07a1bda1..36ec56c3 100644 --- a/source/repl/execute.cpp +++ b/source/repl/execute.cpp @@ -18,7 +18,7 @@ // defined in codegen/directives.cpp fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, - const Identifier& fname, fir::interp::InterpState* is = 0); + const fir::Name& fname, fir::interp::InterpState* is = 0); namespace repl { @@ -121,7 +121,7 @@ namespace repl // note: usually, visitDeclarables in the top-level typecheck will set the realScope. // BUT, since we're not doing that, we must set it manually! if(auto def = dcast(ast::Parameterisable, stmt); def) - def->enclosingScope = state->fs->getCurrentScope2(); + def->enclosingScope = state->fs->scope(); tcr = stmt->typecheck(state->fs); } @@ -180,7 +180,7 @@ namespace repl // (potentially) populate the globalInitPieces, before calling cs->finishGlobalInits(). basically, // it's all handled. auto value = magicallyRunExpressionAtCompileTime(state->cs, *stmt, nullptr, - Identifier(zpr::sprint("__anon_runner_%d", state->fnCounter++), IdKind::Name), + fir::Name::obfuscate("__anon_runner_", state->fnCounter++), state->interpState); state->interpState->finalise(); diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index d4f8000d..05684c63 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -37,7 +37,7 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope2 = this->enclosingScope; + defn->id.scope = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; @@ -63,7 +63,7 @@ TCResult ast::ClassDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* } - auto cls = fir::ClassType::createWithoutBody(defn->id); + auto cls = fir::ClassType::createWithoutBody(defn->id.convertToName()); defn->type = cls; @@ -363,7 +363,7 @@ TCResult ast::ClassDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co for(auto sub : from->subtrees) { if(to->subtrees.find(sub.first) == to->subtrees.end()) - to->subtrees[sub.first] = util::pool(sub.first, to); + to->findOrCreateSubtree(sub.first); recursivelyImport(sub.second, to->subtrees[sub.first]); } diff --git a/source/typecheck/directives.cpp b/source/typecheck/directives.cpp index f8833506..aa311d8b 100644 --- a/source/typecheck/directives.cpp +++ b/source/typecheck/directives.cpp @@ -43,7 +43,7 @@ TCResult ast::RunDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) // defined in codegen/directives.cpp fir::ConstantValue* magicallyRunExpressionAtCompileTime(cgn::CodegenState* cs, sst::Stmt* stmt, fir::Type* infer, - const Identifier& fname, fir::interp::InterpState* is = 0); + const fir::Name& fname, fir::interp::InterpState* is = 0); static size_t condCounter = 0; TCResult ast::IfDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) @@ -77,7 +77,7 @@ TCResult ast::IfDirective::typecheck(sst::TypecheckState* fs, fir::Type* infer) auto cond = c.cond->typecheck(fs, fir::Type::getBool()).expr(); auto value = magicallyRunExpressionAtCompileTime(cs, cond, fir::Type::getBool(), - util::obfuscateIdentifier("interp_if_cond", condCounter++)); + fir::Name::obfuscate("interp_if_cond", condCounter++)); if(!value->getType()->isBoolType()) error(c.cond, "expression with non-boolean type '%s' cannot be used as a conditional in an #if", value->getType()); diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index e73cc827..60e9a782 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -797,7 +797,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, { fs->pushSelfContext(def->type); - auto oldscope = fs->getCurrentScope2(); + auto oldscope = fs->scope(); auto newscope = typdef->innerScope; fs->pushGenericContext(); @@ -858,14 +858,14 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } // dot-op on the union to access its variants; we need constructor stuff for it. - auto oldscope = fs->getCurrentScope2(); + auto oldscope = fs->scope(); auto newscope = unn->innerScope; return checkRhs2(fs, dot, oldscope, newscope, infer); } else if(auto enm = dcast(sst::EnumDefn, def)) { - auto oldscope = fs->getCurrentScope2(); + auto oldscope = fs->scope(); auto newscope = enm->innerScope; auto rhs = checkRhs2(fs, dot, oldscope, newscope, infer); @@ -892,8 +892,8 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } else if(auto scp = dcast(sst::ScopeExpr, left)) { - auto oldscope = fs->getCurrentScope2(); - auto newscope = scp->scope2; + auto oldscope = fs->scope(); + auto newscope = scp->scope; return checkRhs2(fs, dot, oldscope, newscope, infer); } diff --git a/source/typecheck/enums.cpp b/source/typecheck/enums.cpp index 9fe8c11b..5cf7bd86 100644 --- a/source/typecheck/enums.cpp +++ b/source/typecheck/enums.cpp @@ -40,14 +40,14 @@ TCResult ast::EnumDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->bareName = this->name; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope2 = this->enclosingScope; + defn->id.scope = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; defn->innerScope = this->enclosingScope.appending(defnname); // set it to void first, because we want to defer typechecking the member type. - defn->type = fir::EnumType::get(defn->id, fir::Type::getVoid()); + defn->type = fir::EnumType::get(defn->id.convertToName(), fir::Type::getVoid()); if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) return TCResult(err); @@ -95,7 +95,7 @@ TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con auto ecd = util::pool(cs.loc); ecd->id = Identifier(cs.name, IdKind::Name); - ecd->id.scope2 = fs->getCurrentScope2(); + ecd->id.scope = fs->scope(); ecd->type = ety; ecd->parentEnum = defn; ecd->val = val; diff --git a/source/typecheck/function.cpp b/source/typecheck/function.cpp index 8e6640ef..4af380d8 100644 --- a/source/typecheck/function.cpp +++ b/source/typecheck/function.cpp @@ -60,8 +60,9 @@ TCResult ast::FuncDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->bareName = this->name; defn->id = Identifier(this->name, IdKind::Function); - defn->id.scope2 = this->enclosingScope; + defn->id.scope = this->enclosingScope; defn->id.params = ptys; + defn->id.returnType = retty; defn->enclosingScope = this->enclosingScope; defn->params = ps; @@ -139,7 +140,7 @@ TCResult ast::FuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con fs->teleportInto(defn->enclosingScope); fs->enterFunctionBody(defn); - fs->pushTree(defn->id.mangledName()); + fs->pushTree(defn->id.convertToName().mangledWithoutScope()); { // add the arguments to the tree @@ -147,7 +148,7 @@ TCResult ast::FuncDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, con { auto vd = util::pool(arg.loc); vd->id = Identifier(arg.name, IdKind::Name); - vd->id.scope2 = fs->getCurrentScope2(); + vd->id.scope = fs->scope(); vd->type = arg.type; @@ -311,7 +312,7 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) { if(auto p = dcast(Parameterisable, stmt); p) { - p->enclosingScope = fs->getCurrentScope2(); + p->enclosingScope = fs->scope(); } auto tcr = stmt->typecheck(fs); @@ -326,7 +327,7 @@ TCResult ast::Block::typecheck(sst::TypecheckState* fs, fir::Type* inferred) { if(auto p = dcast(Parameterisable, dstmt); p) { - p->enclosingScope = fs->getCurrentScope2(); + p->enclosingScope = fs->scope(); } auto tcr = dstmt->typecheck(fs); diff --git a/source/typecheck/literals.cpp b/source/typecheck/literals.cpp index b1851c1e..220574d2 100644 --- a/source/typecheck/literals.cpp +++ b/source/typecheck/literals.cpp @@ -121,7 +121,7 @@ TCResult ast::LitTuple::typecheck(sst::TypecheckState* fs, fir::Type* infer) auto ty = expr->type; if(expr->type->isConstantNumberType() && (!inf || !inf->isPrimitiveType())) - ty = fs->inferCorrectTypeForLiteral(expr->type->toConstantNumberType()); + ty = sst::inferCorrectTypeForLiteral(expr->type->toConstantNumberType()); vals.push_back(expr); fts.push_back(ty); @@ -215,7 +215,7 @@ TCResult ast::LitArray::typecheck(sst::TypecheckState* fs, fir::Type* infer) if(!elmty) { if(e->type->isConstantNumberType() && !infer) - elmty = fs->inferCorrectTypeForLiteral(e->type->toConstantNumberType()); + elmty = sst::inferCorrectTypeForLiteral(e->type->toConstantNumberType()); else elmty = e->type; diff --git a/source/typecheck/misc.cpp b/source/typecheck/misc.cpp index c13272e0..8f03e081 100644 --- a/source/typecheck/misc.cpp +++ b/source/typecheck/misc.cpp @@ -29,7 +29,7 @@ TCResult ast::PlatformDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) defn->type = ty; defn->id = Identifier(this->typeName, IdKind::Type); - defn->id.scope2 = fs->getCurrentScope2(); + defn->id.scope = fs->scope(); fs->checkForShadowingOrConflictingDefinition(defn, [](sst::TypecheckState* fs, sst::Defn* other) -> bool { return true; }); diff --git a/source/typecheck/polymorph/driver.cpp b/source/typecheck/polymorph/driver.cpp index 94ea30f6..91e39bc6 100644 --- a/source/typecheck/polymorph/driver.cpp +++ b/source/typecheck/polymorph/driver.cpp @@ -79,7 +79,7 @@ namespace poly util::hash_map paramOrder; auto inputcopy = input; - auto selfty = fir::PolyPlaceholderType::get(util::obfuscateName("self_infer"), getNextSessionId()); + auto selfty = fir::PolyPlaceholderType::get(fir::obfuscateName("self_infer"), getNextSessionId()); inputcopy.insert(inputcopy.begin(), FnCallArgument(fs->loc(), "", util::pool(fs->loc(), selfty->getMutablePointerTo()), 0)); diff --git a/source/typecheck/structs.cpp b/source/typecheck/structs.cpp index 0caf65a5..13449908 100644 --- a/source/typecheck/structs.cpp +++ b/source/typecheck/structs.cpp @@ -49,7 +49,7 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope2 = this->enclosingScope; + defn->id.scope = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; @@ -62,7 +62,7 @@ TCResult ast::StructDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type m->enclosingScope = defn->innerScope; } - auto str = fir::StructType::createWithoutBody(defn->id, /* isPacked: */ this->attrs.has(attr::PACKED)); + auto str = fir::StructType::createWithoutBody(defn->id.convertToName(), /* isPacked: */ this->attrs.has(attr::PACKED)); defn->type = str; if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index b77c3dc5..ce91013b 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -50,19 +50,19 @@ namespace sst } } - struct ImportMetadata + struct ExportMetadata { Location loc; - std::string name; + std::string module; std::string imported; }; static void checkConflictingDefinitions(Location loc, const char* kind, const sst::StateTree* base, const sst::StateTree* branch, - std::optional importer = { }, std::optional exporter = { }) + std::optional importer = { }, std::optional exporter = { }) { - for(const auto& [ name, defns ] : base->definitions2) + for(const auto& [ name, defns ] : base->definitions) { - if(auto it = branch->definitions2.find(name); it != branch->definitions2.end()) + if(auto it = branch->definitions.find(name); it != branch->definitions.end()) { for(auto d1 : defns) { @@ -79,7 +79,7 @@ namespace sst { error->append(SimpleError::make(MsgType::Note, exporter->loc, "this public import (from the imported module '%s') brings '%s' into scope ...", - exporter->name, exporter->imported)); + exporter->module, exporter->imported)); error->append(SimpleError::make(MsgType::Note, *importer, "... which conflicts with this using/import statement here:")); @@ -92,7 +92,7 @@ namespace sst else if(exporter) { error->append(SimpleError::make(MsgType::Note, exporter->loc, - "most likely caused by this public import here, in module '%s':", exporter->name)); + "most likely caused by this public import here, in module '%s':", exporter->module)); } error->append(SimpleError::make(MsgType::Note, d1->loc, "for reference, here is the (first) " @@ -111,13 +111,13 @@ namespace sst } } - static std::optional getExportInfo(const sst::StateTree* base, const sst::StateTree* branch) + static std::optional getExportInfo(const sst::StateTree* base, const sst::StateTree* branch) { if(auto it = base->reexportMetadata.find(branch); it != base->reexportMetadata.end()) { - return ImportMetadata { - .loc = it->second.first, - .name = it->second.second, + return ExportMetadata { + .loc = it->second, + .module = base->moduleName, .imported = branch->name }; } @@ -125,7 +125,7 @@ namespace sst } static void checkExportsRecursively(const Location& loc, const char* kind, sst::StateTree* base, sst::StateTree* branch, - std::optional importer = { }, std::optional exporter = { }) + std::optional importer = { }, std::optional exporter = { }) { if(branch->isAnonymous || branch->isCompilerGenerated) return; @@ -149,7 +149,7 @@ namespace sst { std::optional importer; if(auto it = base->importMetadata.find(import); it != base->importMetadata.end()) - importer = it->second.first; + importer = it->second; // check that the new tree doesn't trample over it. checkConflictingDefinitions(loc, kind, import, branch, importer); @@ -185,7 +185,7 @@ namespace sst // no problem -- attach the trees base->imports.push_back(branch); - base->importMetadata[branch] = { loc, base->name }; + base->importMetadata[branch] = loc; // merge the subtrees as well. for(const auto& [ name, tr ] : branch->subtrees) @@ -213,12 +213,12 @@ namespace sst const std::vector>& imports, bool addPreludeDefinitions) { auto tree = new StateTree(file.moduleName, nullptr); + tree->moduleName = file.moduleName; + auto fs = new TypecheckState(tree); for(auto [ ithing, import ] : imports) { - // info(ithing.loc, "(%s) import: %s", file.name, ithing.name); - auto ias = ithing.importAs; if(ias.empty()) ias = cs->parsed[ithing.name].modulePath + cs->parsed[ithing.name].moduleName; @@ -249,7 +249,7 @@ namespace sst } else { - auto newinspt = util::pool(impas, curinspt); + auto newinspt = curinspt->findOrCreateSubtree(impas); curinspt->subtrees[impas] = newinspt; curinspt = newinspt; @@ -266,7 +266,7 @@ namespace sst if(ithing.pubImport) { insertPoint->reexports.push_back(import->base); - insertPoint->reexportMetadata[import->base] = { ithing.loc, file.moduleName }; + insertPoint->reexportMetadata[import->base] = ithing.loc; } fs->dtree->thingsImported.insert(ithing.name); @@ -408,7 +408,7 @@ static void visitDeclarables(sst::TypecheckState* fs, ast::TopLevelBlock* top) { if(auto decl = dcast(ast::Parameterisable, stmt)) { - decl->enclosingScope = fs->getCurrentScope2(); + decl->enclosingScope = fs->scope(); decl->generateDeclaration(fs, 0, { }); } diff --git a/source/typecheck/traits.cpp b/source/typecheck/traits.cpp index 1d908c9a..3e77c850 100644 --- a/source/typecheck/traits.cpp +++ b/source/typecheck/traits.cpp @@ -26,7 +26,7 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope2 = this->enclosingScope; + defn->id.scope = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; @@ -39,7 +39,7 @@ TCResult ast::TraitDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* m->enclosingScope = defn->innerScope; } - auto str = fir::TraitType::create(defn->id); + auto str = fir::TraitType::create(defn->id.convertToName()); defn->type = str; if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) diff --git a/source/typecheck/type.cpp b/source/typecheck/type.cpp index 30bf2ac4..e133d2f8 100644 --- a/source/typecheck/type.cpp +++ b/source/typecheck/type.cpp @@ -15,7 +15,7 @@ namespace sst { - fir::Type* TypecheckState::inferCorrectTypeForLiteral(fir::ConstantNumberType* type) + fir::Type* inferCorrectTypeForLiteral(fir::ConstantNumberType* type) { auto ty = type->toConstantNumberType(); { @@ -47,8 +47,7 @@ namespace sst static ErrorMsg* _complainNoParentScope(TypecheckState* fs, const std::string& top) { - return SimpleError::make(fs->loc(), "invalid use of '^' at the topmost scope '%s'", top) - ->append(BareError::make(MsgType::Note, "current scope is '%s'", fs->serialiseCurrentScope())); + return SimpleError::make(fs->loc(), "invalid use of '^' at the topmost scope '%s'", top); } diff --git a/source/typecheck/typecheckstate.cpp b/source/typecheck/typecheckstate.cpp index db521f35..acd99665 100644 --- a/source/typecheck/typecheckstate.cpp +++ b/source/typecheck/typecheckstate.cpp @@ -189,29 +189,15 @@ namespace sst return this->deferBlockNest > 0; } - Scope TypecheckState::getCurrentScope2() + Scope TypecheckState::scope() { return this->stree->getScope(); } - std::string TypecheckState::serialiseCurrentScope() - { - std::deque scope; - StateTree* tree = this->stree; - - while(tree) - { - scope.push_front(tree->name); - tree = tree->parent; - } - - return zfu::join(std::vector(scope.begin(), scope.end()), "::"); - } - std::vector StateTree::getAllDefinitions() { std::vector ret; - for(const auto& [ n, ds ] : this->definitions2) + for(const auto& [ n, ds ] : this->definitions) for(auto d : ds) ret.push_back(d); @@ -220,7 +206,7 @@ namespace sst static void fetchDefinitionsFrom(const std::string& name, StateTree* tree, bool recursively, bool includePrivate, std::vector& out) { - if(auto it = tree->definitions2.find(name); it != tree->definitions2.end()) + if(auto it = tree->definitions.find(name); it != tree->definitions.end()) { std::copy_if(it->second.begin(), it->second.end(), std::back_inserter(out), [includePrivate](Defn* defn) -> bool { return (includePrivate ? true : defn->visibility == VisibilityLevel::Public); @@ -268,7 +254,7 @@ namespace sst void StateTree::addDefinition(const std::string& name, Defn* def, const TypeParamMap_t& gmaps) { - this->definitions2[name].push_back(def); + this->definitions[name].push_back(def); } @@ -353,6 +339,7 @@ namespace sst else { auto newtree = util::pool(name, this, anonymous); + newtree->moduleName = this->moduleName; this->subtrees[name] = newtree; return newtree; } diff --git a/source/typecheck/unions.cpp b/source/typecheck/unions.cpp index 4dc98278..3aef2241 100644 --- a/source/typecheck/unions.cpp +++ b/source/typecheck/unions.cpp @@ -37,14 +37,14 @@ TCResult ast::UnionDefn::generateDeclaration(sst::TypecheckState* fs, fir::Type* defn->attrs = this->attrs; defn->id = Identifier(defnname, IdKind::Type); - defn->id.scope2 = this->enclosingScope; + defn->id.scope = this->enclosingScope; defn->visibility = this->visibility; defn->original = this; defn->enclosingScope = this->enclosingScope; defn->innerScope = this->enclosingScope.appending(defnname); - if(israw) defn->type = fir::RawUnionType::createWithoutBody(defn->id); - else defn->type = fir::UnionType::createWithoutBody(defn->id); + if(israw) defn->type = fir::RawUnionType::createWithoutBody(defn->id.convertToName()); + else defn->type = fir::UnionType::createWithoutBody(defn->id.convertToName()); if(auto err = fs->checkForShadowingOrConflictingDefinition(defn, [](auto, auto) -> bool { return true; })) return TCResult(err); @@ -130,7 +130,7 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co size_t tfn = 0; for(auto [ loc, pty ] : this->transparentFields) { - auto sfd = make_field(util::obfuscateName("transparent_field", tfn++), loc, pty); + auto sfd = make_field(fir::obfuscateName("transparent_field", tfn++), loc, pty); iceAssert(sfd); sfd->isTransparentField = true; @@ -179,7 +179,7 @@ TCResult ast::UnionDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, co vdef->parentUnion = defn; vdef->variantName = variant.first; vdef->id = Identifier(defn->id.name + "::" + variant.first, IdKind::Name); - vdef->id.scope2 = fs->getCurrentScope2(); + vdef->id.scope = fs->scope(); vdefs.push_back({ vdef, std::get<0>(variant.second) }); diff --git a/source/typecheck/using.cpp b/source/typecheck/using.cpp index 5b992e15..98dc5a77 100644 --- a/source/typecheck/using.cpp +++ b/source/typecheck/using.cpp @@ -49,7 +49,7 @@ TCResult ast::UsingStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer) // this happens in cases like `foo::bar` if(auto se = dcast(sst::ScopeExpr, used)) - scopes = se->scope2; + scopes = se->scope; // and this happens in cases like `foo` else if(vr && (td = dcast(sst::TypeDefn, vr->def))) diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index 3fc35ca4..d7ac9849 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -325,7 +325,7 @@ TCResult ast::VarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) defn->attrs = this->attrs; defn->id = Identifier(this->name, IdKind::Name); - defn->id.scope2 = fs->getCurrentScope2(); + defn->id.scope = fs->scope(); defn->immutable = this->immut; defn->visibility = this->visibility; @@ -355,7 +355,7 @@ TCResult ast::VarDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer) { auto t = defn->init->type; if(t->isConstantNumberType()) - t = fs->inferCorrectTypeForLiteral(defn->init->type->toConstantNumberType()); + t = sst::inferCorrectTypeForLiteral(defn->init->type->toConstantNumberType()); defn->type = t; } From 1e7523fce955a0bf4e1eb7f49d857f9ed957288a Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 02:10:04 +0800 Subject: [PATCH 122/129] fix the interp intrinsic naming --- source/fir/interp/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 1eb81bdc..7b546109 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -481,7 +481,7 @@ namespace interp for(const auto& [ id, intr ] : this->module->_getIntrinsicFunctions()) { - auto fname = Name::obfuscate("__interp_intrinsic_", id.str()); + auto fname = Name::of(zpr::sprint("__interp_intrinsic_%s", id.str())); auto fn = this->module->getOrCreateFunction(fname, intr->getType(), fir::LinkageType::ExternalWeak); // interp::compileFunction already maps the newly compiled interp::Function, but since we created a From e2adae8a633fb9d8b110b13221f703d00331ef81 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 02:11:13 +0800 Subject: [PATCH 123/129] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b88055fe..7e571484 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,10 @@ do { ### Building the Flax compiler -Note: the `master` branch is still using LLVM 7, and is stable; `develop` is now on LLVM 9. Changes will be merged into `master` eventually. +Note: the `master` branch is still using LLVM 7, and is stable; `develop` is now on LLVM 11. Changes will be merged into `master` eventually. #### Dependencies #### -- LLVM 9, mostly due to their obsession with changing the IR interface every damn version +- LLVM 11, mostly due to their obsession with changing the IR interface every damn version - GMP/MPIR - MPFR - libffi From 2e065e13f55382968d4115c71f5a4252a530a212 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 02:16:32 +0800 Subject: [PATCH 124/129] try to fix windows build --- source/frontend/parser/expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 3cdb883e..20ee6429 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -103,7 +103,7 @@ namespace parser using namespace attr; - return ret.map([&allowed, &attrs](auto ret) -> auto { + return ret.map([&allowed, &attrs](auto ret) -> Stmt* { // there's probably a better way to do this, but bleugh if((attrs.flags & RAW) && !(allowed.flags & RAW)) error(ret, "unsupported attribute '@raw' on %s", ret->readableName); From 8488a6aeea8811f37d8358a84c1c57b2efbfe671 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 02:23:23 +0800 Subject: [PATCH 125/129] try again --- .travis.yml | 71 -------------------------------- source/include/parser_internal.h | 13 ++---- 2 files changed, 4 insertions(+), 80 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bd9299cc..00000000 --- a/.travis.yml +++ /dev/null @@ -1,71 +0,0 @@ -language: cpp -compiler: clang - -matrix: - include: - - os: linux - dist: xenial - sudo: false - - os: osx - osx_image: xcode11.2 - -cache: - directories: - - llvm - -addons: - apt: - sources: - - ubuntu-toolchain-r-test - - sourceline: deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main - key_url: https://apt.llvm.org/llvm-snapshot.gpg.key - - packages: - - g++-9 - - llvm-9 - - llvm-9-dev - - libllvm9 - - libmpfr-dev - - libmpfr4 - -script: - - | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ ! -d "llvm/9.0.0/bin" ]; then - curl 'https://homebrew.bintray.com/bottles/llvm-9.0.0.high_sierra.bottle.tar.gz' -L -o llvm-9.0.0.tar.gz; - tar -xf llvm-9.0.0.tar.gz; - fi; - echo "clang version:" $($CC --version); - echo "clang path:" $(which $CC); - PATH="$PATH:$(pwd)/llvm/9.0.0/bin" LLVM_CONFIG=llvm-config ENABLE_CODE_COVERAGE=yes make -j2 build; - else - CXX=g++-9 CC=gcc-9 LLVM_CONFIG=llvm-config-9 ENABLE_CODE_COVERAGE=yes make -j2 build; - fi - - LLVM_PROFILE_FILE="tester_llvm.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend llvm build/tester.flx - - LLVM_PROFILE_FILE="tester_interp.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile -run -backend interp build/tester.flx - - LLVM_PROFILE_FILE="tester_compile.profraw" build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot --ffi-escape -profile build/tester.flx && ./tester - - | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - llvm/9.0.0/bin/llvm-profdata merge -sparse tester_llvm.profraw tester_interp.profraw tester_compile.profraw -o tester_all.profdata; - llvm/9.0.0/bin/llvm-cov show ./build/sysroot/usr/local/bin/flaxc -instr-profile=tester_all.profdata > coverage.txt; - else - for filename in `find . -name '*.cpp'`; do gcov-9 -o "$filename".gcno $filename > /dev/null; done - fi - -notifications: - email: false - -after_success: - - zip -r $TRAVIS_OS_NAME-x64 build/sysroot - - bash <(curl -s https://codecov.io/bash) - -deploy: - provider: releases - api-key: ${GITHUB_OAUTH_TOKEN} - file: $TRAVIS_OS_NAME-x64.zip - skip-cleanup: true - on: - all_branches: true - tags: true - - diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 189f6a11..56acfaa8 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -334,18 +334,13 @@ namespace parser return *this; } - template >> - PResult map(const F& fn) const + template + auto map(const F& fn) const -> PResult()))>> { - if(this->state > 0) return PResult(this->error, this->state); - else return PResult(fn(this->value)); - } + using U = std::decay_t()))>; - template ::value_t> - PResult flatmap(const F& fn) const - { if(this->state > 0) return PResult(this->error, this->state); - else return fn(this->value); + else return PResult(fn(this->value)); } ErrorMsg* err() const From d103382d962f68944ca2d13e3a0983f51c1bfc2f Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 10:57:58 +0800 Subject: [PATCH 126/129] fix windows build for real this time --- .gitignore | 1 + libs/libc.flx | 96 +++++++++++++++---------------- meson.build | 72 ++++++++++++----------- source/fir/interp/interpreter.cpp | 4 +- source/typecheck/toplevel.cpp | 2 + 5 files changed, 92 insertions(+), 83 deletions(-) diff --git a/.gitignore b/.gitignore index cb87e3a3..504594cf 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,7 @@ bug-repro *.vcxproj.user countloc.bat +builddir prof.svg prof_with_square.svg diff --git a/libs/libc.flx b/libs/libc.flx index e3e097a2..951a06db 100644 --- a/libs/libc.flx +++ b/libs/libc.flx @@ -8,74 +8,74 @@ export libc public ffi fn puts(fmt: &i8) -> i32 public ffi fn printf(fmt: &i8, ...) -> i32 public ffi fn sprintf(fmt: &i8, y: &i8, ...) -> i32 -// public ffi fn snprintf(fmt: &i8, l: u64, y: &i8, ...) -> i32 +public ffi fn snprintf(fmt: &i8, l: u64, y: &i8, ...) -> i32 -// public ffi fn fprintf(stream: &void, y: &i8, ...) -> i32 +public ffi fn fprintf(stream: &void, y: &i8, ...) -> i32 -// public ffi fn putchar(x: i32) -> i32 +public ffi fn putchar(x: i32) -> i32 -// // memcpy/set/move -// public ffi fn memcpy(dest: &i8, source: &i8, length: u64) -> &i8 -// public ffi fn memmove(dest: &i8, source: &i8, length: u64) -> &i8 -// public ffi fn memset(dest: &i8, value: i32, length: u64) -> &i8 +// memcpy/set/move +public ffi fn memcpy(dest: &i8, source: &i8, length: u64) -> &i8 +public ffi fn memmove(dest: &i8, source: &i8, length: u64) -> &i8 +public ffi fn memset(dest: &i8, value: i32, length: u64) -> &i8 -// // heap -// // public ffi fn malloc(size: i64) -> &i8 -// // public ffi fn free(pointer: &i8) +// heap +// public ffi fn malloc(size: i64) -> &i8 +// public ffi fn free(pointer: &i8) -// // strings -// public ffi fn strlen(s: &i8) -> i64 -// public ffi fn strcmp(s1: &i8, s2: &i8) -> i32 -// public ffi fn strncmp(s1: &i8, s2: &i8, length: i64) -> i32 +// strings +public ffi fn strlen(s: &i8) -> i64 +public ffi fn strcmp(s1: &i8, s2: &i8) -> i32 +public ffi fn strncmp(s1: &i8, s2: &i8, length: i64) -> i32 -// // random things -// public ffi fn usleep(usec: u32) -> i32 -// public ffi fn sleep(sec: u32) -> u32 +// random things +public ffi fn usleep(usec: u32) -> i32 +public ffi fn sleep(sec: u32) -> u32 public ffi fn abort() -// public ffi fn exit(status: i32) +public ffi fn exit(status: i32) -// // file stuff -// public ffi fn fsync(fd: i32) -> i32 -// public ffi fn fflush(fd: &void) -> i32 -// public ffi fn ioctl(fd: i32, cmd: u64, ...) -> i32 +// file stuff +public ffi fn fsync(fd: i32) -> i32 +public ffi fn fflush(fd: &void) -> i32 +public ffi fn ioctl(fd: i32, cmd: u64, ...) -> i32 -// public ffi fn fopen(path: &i8, mode: &i8) -> &void -// public ffi fn fread(buf: &i8, sz: u64, cnt: u64, file: &void) -> u64 -// public ffi fn fclose(file: &void) -> i32 +public ffi fn fopen(path: &i8, mode: &i8) -> &void +public ffi fn fread(buf: &i8, sz: u64, cnt: u64, file: &void) -> u64 +public ffi fn fclose(file: &void) -> i32 -// // unistd.h +// unistd.h -// // posix stuff, that windows likes to rename for some reason!!!!! -// #if os::name == "windows" -// { -// // unistd.h -// public ffi fn open(path: &i8, flags: i32, mode: i32) -> i32 as "_open" -// public ffi fn close(fd: i32) -> i32 as "_close" +// posix stuff, that windows likes to rename for some reason!!!!! +#if os::name == "windows" +{ + // unistd.h + public ffi fn open(path: &i8, flags: i32, mode: i32) -> i32 as "_open" + public ffi fn close(fd: i32) -> i32 as "_close" -// public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 as "_read" -// public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 as "_write" + public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 as "_read" + public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 as "_write" -// public ffi fn lseek(fd: i32, ofs: i64, whence: i32) -> i64 as "_lseek" + public ffi fn lseek(fd: i32, ofs: i64, whence: i32) -> i64 as "_lseek" -// // stdio.h -// public ffi fn fdopen(fd: i32, mode: &i8) -> &void as "_fdopen" -// } -// else -// { -// // unistd.h -// public ffi fn open(path: &i8, flags: i32, mode: i32) -> i32 -// public ffi fn close(fd: i32) -> i32 + // stdio.h + public ffi fn fdopen(fd: i32, mode: &i8) -> &void as "_fdopen" +} +else +{ + // unistd.h + public ffi fn open(path: &i8, flags: i32, mode: i32) -> i32 + public ffi fn close(fd: i32) -> i32 -// public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 + public ffi fn read(fd: i32, buf: &i8, count: i64) -> i64 public ffi fn write(fd: i32, buf: &i8, count: i64) -> i64 -// public ffi fn lseek(fd: i32, ofs: i64, whence: i32) -> i64 + public ffi fn lseek(fd: i32, ofs: i64, whence: i32) -> i64 -// // stdio.h -// public ffi fn fdopen(fd: i32, mode: &i8) -> &void -// } + // stdio.h + public ffi fn fdopen(fd: i32, mode: &i8) -> &void +} diff --git a/meson.build b/meson.build index 765147b1..fc4943aa 100644 --- a/meson.build +++ b/meson.build @@ -61,7 +61,7 @@ if the_compiler.get_id() == 'msvc' envname_llvm = '%LLVM_ROOT_DIR%' envname_libffi = '%LIBFFI_ROOT_DIR%' - llvm_version = '9.0.0' + llvm_version = '11.0.0' mpir_root_dir = run_command('cmd.exe', '/C', 'echo', envname_mpir).stdout().strip() if mpir_root_dir == envname_mpir @@ -111,46 +111,51 @@ if the_compiler.get_id() == 'msvc' llvm_dep = declare_dependency(version: llvm_version, include_directories: include_directories(llvm_hdr_dir), dependencies: [ mpfr_dep, mpir_dep, - the_compiler.find_library('LLVMMC', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMipo', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMCore', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMCJIT', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMOrcJIT', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMLinker', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMTarget', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMObject', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMPasses', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86Info', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMCodegen', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86Desc', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMSupport', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMRemarks', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMCParser', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86Utils', dirs: llvm_lib_dir), the_compiler.find_library('LLVMAnalysis', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMTablegen', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMDemangle', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMVectorize', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMAsmPrinter', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMBinaryFormat', dirs: llvm_lib_dir), the_compiler.find_library('LLVMBitReader', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMLibDriver', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMBitstreamReader', dirs: llvm_lib_dir), the_compiler.find_library('LLVMBitWriter', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMAsmPrinter', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMCFGuard', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMCodegen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMCore', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoCodeView', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoDWARF', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoGSYM', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoPDB', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDebugInfoMSF', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMDemangle', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMExecutionEngine', dirs: llvm_lib_dir), the_compiler.find_library('LLVMGlobalISel', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMScalarOpts', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86CodeGen', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMProfileData', dirs: llvm_lib_dir), the_compiler.find_library('LLVMInstCombine', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMipo', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMJITLink', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMLibDriver', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMLinker', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMC', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMCDisassembler', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMCJIT', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMMCParser', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMObject', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMOrcError', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMOrcJIT', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMPasses', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMProfileData', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMRemarks', dirs: llvm_lib_dir), the_compiler.find_library('LLVMRuntimeDyld', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMScalarOpts', dirs: llvm_lib_dir), the_compiler.find_library('LLVMSelectionDAG', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMBinaryFormat', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMX86AsmParser', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMDebugInfoPDB', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMDebugInfoDWARF', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMSupport', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMTablegen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMTarget', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMTextAPI', dirs: llvm_lib_dir), the_compiler.find_library('LLVMTransformUtils', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMMCDisassembler', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMExecutionEngine', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMBitstreamReader', dirs: llvm_lib_dir), - the_compiler.find_library('LLVMDebugInfoCodeView', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMVectorize', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86AsmParser', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86CodeGen', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86Desc', dirs: llvm_lib_dir), + the_compiler.find_library('LLVMX86Info', dirs: llvm_lib_dir), ] ) @@ -316,6 +321,7 @@ source_files = files([ 'source/fir/IRBlock.cpp', 'source/fir/Module.cpp', 'source/fir/Value.cpp', + 'source/fir/Name.cpp', 'source/fir/Types/DynamicArrayType.cpp', 'source/fir/Types/ArraySliceType.cpp', diff --git a/source/fir/interp/interpreter.cpp b/source/fir/interp/interpreter.cpp index 7b546109..84d2b18a 100644 --- a/source/fir/interp/interpreter.cpp +++ b/source/fir/interp/interpreter.cpp @@ -505,10 +505,10 @@ namespace interp for(const auto& name : names) { - auto fn = this->module->getFunction(Identifier(name, IdKind::Name)); + auto fn = this->module->getFunction(Name::of(name)); if(fn) { - auto wrapper = this->module->getOrCreateFunction(Identifier("__interp_wrapper_" + name, IdKind::Name), + auto wrapper = this->module->getOrCreateFunction(Name::of(zpr::sprint("__interp_wrapper_%s", name)), fn->getType()->toFunctionType(), fir::LinkageType::ExternalWeak); this->compiledFunctions[fn] = this->compileFunction(wrapper); diff --git a/source/typecheck/toplevel.cpp b/source/typecheck/toplevel.cpp index ce91013b..40a07b00 100644 --- a/source/typecheck/toplevel.cpp +++ b/source/typecheck/toplevel.cpp @@ -2,6 +2,8 @@ // Copyright (c) 2014 - 2017, zhiayang // Licensed under the Apache License Version 2.0. +#include + #include "defs.h" #include "sst.h" #include "ast.h" From f16f03de41ff467edbaebef42a70421ff2fe26f2 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 11:11:06 +0800 Subject: [PATCH 127/129] poke the appveyor cache --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 574f7ffb..a1bd952c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,9 +9,11 @@ environment: LLVM_ROOT_DIR: c:\projects\lib\llvm LIBFFI_ROOT_DIR: c:\projects\lib\libffi + cache: - c:\projects\lib -> appveyor.yml + install: # Set up the build environment - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 From 5e50c8357e3dddccf8e6ab2d4464c7e053b5f673 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 11:31:17 +0800 Subject: [PATCH 128/129] update CI configs, and update readme too --- .semaphore/semaphore.yml | 6 +++--- README.md | 11 ----------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index ee8bbf0f..b477f677 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -15,11 +15,11 @@ blocks: - name: build commands: - checkout - - sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list + - sudo echo "deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main" | sudo tee -a /etc/apt/sources.list - sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo apt -y update - - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-9 llvm-9-dev libllvm9 libmpfr-dev libmpfr6 - - CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-9 make -j2 build + - sudo apt-get -o Dpkg::Options::="--force-overwrite" --allow-unauthenticated -y install -y llvm-11 llvm-11-dev libllvm11 libmpfr-dev libmpfr6 + - CXX=g++-8 CC=gcc-8 LLVM_CONFIG=llvm-config-11 make -j2 build - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend llvm build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape -run -backend interp build/tester.flx - build/sysroot/usr/local/bin/flaxc -sysroot build/sysroot -profile --ffi-escape build/tester.flx && ./tester diff --git a/README.md b/README.md index 7e571484..18bc2f08 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,10 @@ A low level, general-purpose language with high level syntax and expressibility. [![forthebadge](https://forthebadge.com/images/badges/made-with-crayons.svg)](http://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](http://forthebadge.com) -[![build status](https://travis-ci.org/flax-lang/flax.svg?branch=develop)](https://travis-ci.org/flax-lang/flax) -     [![build status](https://ci.appveyor.com/api/projects/status/c9cmm08t27ef1hji/branch/develop?svg=true)](https://ci.appveyor.com/project/zhiayang/flax/branch/develop)      [![build status](https://github.com/flax-lang/flax/workflows/CI/badge.svg)](https://github.com/flax-lang/flax/actions)      -[![codecov](https://codecov.io/gh/flax-lang/flax/branch/develop/graph/badge.svg)](https://codecov.io/gh/flax-lang/flax) -     -[![language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/flax-lang/flax.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/flax-lang/flax/context:cpp) - -Currently not expected to pass (for a while):
[![Build Status](https://semaphoreci.com/api/v1/zhiayang/flax/branches/develop/badge.svg)](https://semaphoreci.com/zhiayang/flax) @@ -33,8 +26,6 @@ Currently not expected to pass (for a while):
I work on Flax in my spare time, and as the lone developer I cannot guarantee continuous development. I'm no famous artist but this is my magnum opus, so it'll not be abandoned anytime soon. -Development is currently on hiatus. Regular work will resume NET 2019-12-09. - ### Language Goals - No header files. @@ -116,8 +107,6 @@ do { ### Building the Flax compiler -Note: the `master` branch is still using LLVM 7, and is stable; `develop` is now on LLVM 11. Changes will be merged into `master` eventually. - #### Dependencies #### - LLVM 11, mostly due to their obsession with changing the IR interface every damn version - GMP/MPIR From 6425002926bf49a34483b93aacd31b7a8523a06d Mon Sep 17 00:00:00 2001 From: zhiayang Date: Tue, 1 Dec 2020 12:08:17 +0800 Subject: [PATCH 129/129] stuff --- build/tests/basic.flx | 6 +-- build/tmp2/b.flx | 5 +++ build/tmp2/c.flx | 3 ++ notes.md | 69 ++++++++++++++++------------- source/frontend/parser/function.cpp | 2 + source/frontend/parser/type.cpp | 6 ++- 6 files changed, 57 insertions(+), 34 deletions(-) diff --git a/build/tests/basic.flx b/build/tests/basic.flx index ed3af988..60ac2cc2 100644 --- a/build/tests/basic.flx +++ b/build/tests/basic.flx @@ -115,10 +115,10 @@ public fn doBasicTest() var c: any = 30 println("c.typeid = %, c.refcount = %", c.id, c.refcount) - enum E + enum E: u32 { - case ONE - case TWO + case ONE = 100 + case TWO = 200 } var e = E::TWO diff --git a/build/tmp2/b.flx b/build/tmp2/b.flx index a2777ca0..9c131733 100644 --- a/build/tmp2/b.flx +++ b/build/tmp2/b.flx @@ -2,6 +2,11 @@ // Copyright (c) 2020, zhiayang // Licensed under the Apache License Version 2.0. +<<<<<<< Updated upstream public import c as _ public fn bazzle() -> int => 30 +======= +public let INIT_TIMER: u32 = 0x00000001 +public let INIT_AUDIO: u32 = 0x00000010 +>>>>>>> Stashed changes diff --git a/build/tmp2/c.flx b/build/tmp2/c.flx index 7e7bd296..8fa0af9a 100644 --- a/build/tmp2/c.flx +++ b/build/tmp2/c.flx @@ -2,6 +2,7 @@ // Copyright (c) 2020, zhiayang // Licensed under the Apache License Version 2.0. +<<<<<<< Updated upstream export c ffi fn printf(fmt: &i8, ...) -> int @@ -10,3 +11,5 @@ public fn foozle(x: int) { printf("hello, world! (%d)\n", x) } +======= +>>>>>>> Stashed changes diff --git a/notes.md b/notes.md index 8a656b13..645d5dcf 100644 --- a/notes.md +++ b/notes.md @@ -1,47 +1,56 @@ ## notes -each scope already exists as a StateTree, so, whenever a public definition is encountered, it is added to the -`exports` list of its tree. +## to fix -when importing another module: -1. if the module is imported `as _`, add the module's StateTree to the current (toplevel) scope's `imports` list -2. if the module is imported `as foo::bar`, add the module's StateTree to the `imports` list of `foo::bar` (creating if needed) +1. `public static` doesn't work, but `static public` works. -3. do a (complete) tree traversal "in-step" with the current scope, to check for duplicate (incompatbile) definitions. - - big oof, but if we want the current module "paradigm" to work, this has to be done. +2. underlining breaks for multi-line spans; just don't underline in that case. -(done) when resolving a definition: -1. follow the same resolution order (deepest-to-widest) -2. after checking each level, additionally check the list of imports. -3. of course, we should not traverse "down" into the imported tree -- only look at its top-level defs - - we need to be going up, not down! -4. for each imported tree, we should only check definitions that are in its `exports` list +3. types don't appear before functions for some reason, for typechecking. (ie. order becomes important) -to refactor using: -we should simply treat it as a scope-level import, ie. for `using foo::bar as qux`, we just create a new scope -`qux`, then attach `foo::bar` to `qux` by appending to its `imports` list. here, we should check that `qux` does -not already exist. -(this is different for imports, where two imported modules are able merge their exported namespaces. for `using`, -i'm not too sure that's a good idea, but it's easy enough to change if needed) +4. enum values are not working correctly (seems to be right-shifted by 8?) + (the values are not assigned) -for `using foo::bar as _`, we do the same thing as `import as _`, and just attach the tree of `foo::bar` to -the imports list of the current scope. +5. defer appears to be broken: +``` +var i = 0 +while true { + defer i += 1 + // doesn't change +} +``` +6. "unsynchronised use of global init function!!!" -- need to figure out a way to serialise access to the global init function -## to fix +7. polymorphic stuff breaks when manually instantiating -1. `public static` doesn't work, but `static public` works. +8. compiler crash: +``` +import libc as _ -2. underlining breaks for multi-line spans; just don't underline in that case. +struct Cat { + fn greet() { + printf("i love manga uwu\n") + } +} +@entry fn main() { + let c: Cat! = Cat() -## to refactor + c.greet(); +} +``` -2. a lot of places probably still have the concept of `scope == std::vector`. - - after the first scope refactor, i think these instances will be reduced - - undoubtedly there will be more. for instance, Identifier holds the scope as exactly that. - (but, is there actually a need for it to be anything else? it really is just an identifier, after all) +9. ambiguous call to initialiser of class + +10. assignment to runtime variable in #run block + +11. numbers basically don't cast properly at all + + + +## to refactor 3. all uses of defer() need to be audited. there are instances where an exception is thrown during typechecking as a result of unwrapping a `TCResult` that contains an error; as the stack unwinds, it may encounter a @@ -68,5 +77,5 @@ the imports list of the current scope. ## to investigate -1. we rely on a lot of places to set `realScope` (and `enclosingScope`) correctly when typechecking structs etc. there should +1. we rely on a lot of places to set `enclosingScope` correctly when typechecking structs etc. there should be a better way to do this, probably automatically or something like that. basically anything except error-prone manual setting. diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index e0449ca8..016fbe8a 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -43,6 +43,8 @@ namespace parser bool startedOptional = false; while(st.front() != TT::RParen) { + st.skipWS(); + if(iscvar || isfvar) error(st, "variadic parameter list must be the last function parameter"); diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index 112328ce..679eca32 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -483,7 +483,11 @@ namespace parser if(st.frontAfterWS() == TT::Equal) { if(memberType == 0) - error(st.loc(), "enumeration member type must be specified when assigning explicit values to cases"); + { + SimpleError::make(st.loc(), "enumeration member type must be specified when assigning explicit values to cases") + ->append(SimpleError::make(MsgType::Note, idloc, "add the type here")) + ->postAndQuit(); + } // ok, parse a value st.eat();