mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	- updated func/fift - additional checks in block validator - docs - tunnel prototype in ADNL
		
			
				
	
	
		
			3167 lines
		
	
	
	
		
			88 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3167 lines
		
	
	
	
		
			88 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 
 | |
|     This file is part of TON Blockchain source code.
 | |
| 
 | |
|     TON Blockchain 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 2
 | |
|     of the License, or (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain 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 TON Blockchain.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     In addition, as a special exception, the copyright holders give permission 
 | |
|     to link the code of portions of this program with the OpenSSL library. 
 | |
|     You must obey the GNU General Public License in all respects for all 
 | |
|     of the code used other than OpenSSL. If you modify file(s) with this 
 | |
|     exception, you may extend this exception to your version of the file(s), 
 | |
|     but you are not obligated to do so. If you do not wish to do so, delete this 
 | |
|     exception statement from your version. If you delete this exception statement 
 | |
|     from all source files in the program, then also delete it here.
 | |
| 
 | |
|     Copyright 2017-2020 Telegram Systems LLP
 | |
| */
 | |
| #include <vector>
 | |
| #include <string>
 | |
| #include <map>
 | |
| #include <set>
 | |
| #include <stack>
 | |
| #include <utility>
 | |
| #include <algorithm>
 | |
| #include <iostream>
 | |
| #include <fstream>
 | |
| #include <functional>
 | |
| #include <sstream>
 | |
| #include <cstdio>
 | |
| #include <cassert>
 | |
| #include <cstdlib>
 | |
| #include <getopt.h>
 | |
| 
 | |
| #include "common/refcnt.hpp"
 | |
| #include "common/bigint.hpp"
 | |
| #include "common/refint.h"
 | |
| #include "parser/srcread.h"
 | |
| #include "parser/lexer.h"
 | |
| #include "parser/symtable.h"
 | |
| #include "td/utils/Slice-decl.h"
 | |
| #include "td/utils/format.h"
 | |
| #include "td/utils/crypto.h"
 | |
| #include "tlbc-aux.h"
 | |
| #include "tlbc-data.h"
 | |
| #include "tlbc-gen-cpp.h"
 | |
| 
 | |
| int verbosity;
 | |
| 
 | |
| namespace src {
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *   KEYWORD DEFINITION
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| enum { _Eof = -1, _Ident = 0, _Number, _Special, _Eq = 0x80, _Leq, _Geq, _Neq, _Type, _EMPTY };
 | |
| 
 | |
| void define_keywords() {
 | |
|   sym::symbols.add_kw_char('+')
 | |
|       .add_kw_char('-')
 | |
|       .add_kw_char('*')
 | |
|       .add_kw_char(':')
 | |
|       .add_kw_char(';')
 | |
|       .add_kw_char('(')
 | |
|       .add_kw_char(')')
 | |
|       .add_kw_char('{')
 | |
|       .add_kw_char('}')
 | |
|       .add_kw_char('[')
 | |
|       .add_kw_char(']')
 | |
|       .add_kw_char('=')
 | |
|       .add_kw_char('_')
 | |
|       .add_kw_char('?')
 | |
|       .add_kw_char('.')
 | |
|       .add_kw_char('~')
 | |
|       .add_kw_char('^');
 | |
| 
 | |
|   sym::symbols.add_keyword("==", _Eq)
 | |
|       .add_keyword("<=", _Leq)
 | |
|       .add_keyword(">=", _Geq)
 | |
|       .add_keyword("!=", _Neq)
 | |
|       .add_keyword("Type", _Type)
 | |
|       .add_keyword("EMPTY", _EMPTY);
 | |
| }
 | |
| 
 | |
| // parses constant bitstrings in format \#[0-9a-f]*_? or \$[01]*_?
 | |
| unsigned long long get_special_value(std::string str) {
 | |
|   std::size_t i = 1, n = str.size();
 | |
|   if (n <= 1) {
 | |
|     return 0;
 | |
|   }
 | |
|   unsigned long long val = 0;
 | |
|   int bits = 0;
 | |
|   if (str[0] == '#') {
 | |
|     for (; i < n; i++) {
 | |
|       int c = str[i];
 | |
|       if (c == '_') {
 | |
|         break;
 | |
|       }
 | |
|       if (c >= '0' && c <= '9') {
 | |
|         c -= '0';
 | |
|       } else if (c >= 'A' && c <= 'F') {
 | |
|         c -= 'A' - 10;
 | |
|       } else if (c >= 'a' && c <= 'f') {
 | |
|         c -= 'a' - 10;
 | |
|       } else {
 | |
|         return 0;
 | |
|       }
 | |
|       if (bits > 60) {
 | |
|         return 0;
 | |
|       }
 | |
|       val |= (unsigned long long)c << (60 - bits);
 | |
|       bits += 4;
 | |
|     }
 | |
|   } else if (str[0] == '$') {
 | |
|     if (str[1] != '_') {
 | |
|       for (; i < n; i++) {
 | |
|         int c = str[i];
 | |
|         c -= '0';
 | |
|         if (c & -2) {
 | |
|           return 0;
 | |
|         }
 | |
|         if (bits > 63) {
 | |
|           return 0;
 | |
|         }
 | |
|         val |= (unsigned long long)c << (63 - bits);
 | |
|         bits++;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     return 0;
 | |
|   }
 | |
|   if (i < n - 1) {
 | |
|     return 0;
 | |
|   }
 | |
|   if (i == n - 1 && bits) {
 | |
|     // trailing _
 | |
|     while (bits && !((val >> (64 - bits)) & 1)) {
 | |
|       --bits;
 | |
|     }
 | |
|     if (bits) {
 | |
|       --bits;
 | |
|     }
 | |
|   }
 | |
|   if (bits == 64) {
 | |
|     return 0;
 | |
|   }
 | |
|   return val | (1ULL << (63 - bits));
 | |
| }
 | |
| 
 | |
| int lexem_is_special(std::string str) {
 | |
|   return get_special_value(str) ? Lexem::Special : 0;
 | |
| }
 | |
| 
 | |
| }  // namespace src
 | |
| 
 | |
| namespace sym {
 | |
| 
 | |
| enum class IdSc : char { undef = 0, lc = 1, uc = 2, blc = 3 };
 | |
| // subclass:
 | |
| // 1 = first letter or first letter after last . is lowercase
 | |
| // 2 = ... uppercase
 | |
| // 3 = 1 + first character (after last ., if present) is a !
 | |
| // 0 = else
 | |
| int compute_symbol_subclass(std::string str) {
 | |
|   IdSc res = IdSc::undef;
 | |
|   int t = 0, s = 0;
 | |
|   for (char c : str) {
 | |
|     if (c == '.') {
 | |
|       res = IdSc::undef;
 | |
|       s = t = 0;
 | |
|     } else if (res == IdSc::undef) {
 | |
|       if (!s) {
 | |
|         s = (c == '!' ? 1 : -1);
 | |
|       }
 | |
|       if ((c | 0x20) >= 'a' && (c | 0x20) <= 'z') {
 | |
|         res = (c & 0x20 ? IdSc::lc : IdSc::uc);
 | |
|       }
 | |
|       if (t && (((unsigned)c & 0xc0) == 0x80)) {
 | |
|         t = (t << 6) | ((unsigned)c & 0x3f);
 | |
|         if (t >= 0x410 && t < 0x450) {
 | |
|           res = (t < 0x430 ? IdSc::uc : IdSc::lc);
 | |
|         }
 | |
|       }
 | |
|       t = (((unsigned)c & 0xe0) == 0xc0 ? (c & 0x1f) : 0);
 | |
|     }
 | |
|   }
 | |
|   if (s == 1 && res == IdSc::lc) {
 | |
|     res = IdSc::blc;
 | |
|   }
 | |
|   return (int)res;
 | |
| }
 | |
| 
 | |
| inline bool is_lc_ident(sym_idx_t idx) {
 | |
|   auto sc = symbols.get_subclass(idx);
 | |
|   return sc == (int)IdSc::lc || sc == (int)IdSc::blc;
 | |
| }
 | |
| 
 | |
| inline bool is_spec_lc_ident(sym_idx_t idx) {
 | |
|   auto sc = symbols.get_subclass(idx);
 | |
|   return sc == (int)IdSc::blc;
 | |
| }
 | |
| 
 | |
| inline bool is_uc_ident(sym_idx_t idx) {
 | |
|   return symbols.get_subclass(idx) == (int)IdSc::uc;
 | |
| }
 | |
| 
 | |
| }  // namespace sym
 | |
| 
 | |
| namespace tlbc {
 | |
| 
 | |
| td::LinearAllocator AR(1 << 22);
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *  AUXILIARY DATA TYPES
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| // headers are in tlbc-aux.h
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, const BitPfxCollection& p) {
 | |
|   p.show(os);
 | |
|   return os;
 | |
| }
 | |
| 
 | |
| void BitPfxCollection::show(std::ostream& os) const {
 | |
|   char first = '{';
 | |
|   for (unsigned long long val : pfx) {
 | |
|     os << first;
 | |
|     while (val & (All - 1)) {
 | |
|       os << (val >> 63);
 | |
|       val <<= 1;
 | |
|     }
 | |
|     os << '*';
 | |
|     first = ',';
 | |
|   }
 | |
|   if (first == '{') {
 | |
|     os << '{';
 | |
|   }
 | |
|   os << '}';
 | |
| }
 | |
| 
 | |
| void BitPfxCollection::merge_back(unsigned long long z) {
 | |
|   if (!pfx.size()) {
 | |
|     pfx.push_back(z);
 | |
|     return;
 | |
|   }
 | |
|   unsigned long long w = td::lower_bit64(z);
 | |
|   while (pfx.size()) {
 | |
|     unsigned long long t = z ^ pfx.back();
 | |
|     if (!t) {
 | |
|       return;
 | |
|     }
 | |
|     if (t != (w << 1)) {
 | |
|       break;
 | |
|     }
 | |
|     z -= w;
 | |
|     w <<= 1;
 | |
|     pfx.pop_back();
 | |
|   }
 | |
|   pfx.push_back(z);
 | |
| }
 | |
| 
 | |
| BitPfxCollection& BitPfxCollection::operator*=(unsigned long long prepend) {
 | |
|   if (!prepend) {
 | |
|     clear();
 | |
|     return *this;
 | |
|   }
 | |
|   if (prepend == All) {
 | |
|     return *this;
 | |
|   }
 | |
|   int l = 63 - td::count_trailing_zeroes_non_zero64(prepend);
 | |
|   prepend &= prepend - 1;
 | |
|   std::size_t i, j = 0, n = pfx.size();
 | |
|   for (i = 0; i < n; i++) {
 | |
|     unsigned long long z = pfx[i], zw = td::lower_bit64(z);
 | |
|     z >>= l;
 | |
|     z |= prepend;
 | |
|     if (!(zw >> l)) {
 | |
|       z |= 1;
 | |
|     }
 | |
|     if (!j || pfx[j - 1] != z) {
 | |
|       pfx[j++] = z;
 | |
|     }
 | |
|   }
 | |
|   pfx.resize(j);
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| BitPfxCollection BitPfxCollection::operator*(unsigned long long prepend) const {
 | |
|   if (!prepend) {
 | |
|     return BitPfxCollection{};
 | |
|   }
 | |
|   if (prepend == All) {
 | |
|     return *this;
 | |
|   }
 | |
|   BitPfxCollection res;
 | |
|   res.pfx.reserve(pfx.size());
 | |
|   int l = 63 - td::count_trailing_zeroes_non_zero64(prepend);
 | |
|   prepend &= prepend - 1;
 | |
|   std::size_t i, n = pfx.size();
 | |
|   for (i = 0; i < n; i++) {
 | |
|     unsigned long long z = pfx[i], zw = td::lower_bit64(z);
 | |
|     z >>= l;
 | |
|     z |= prepend;
 | |
|     if (!(zw >> l)) {
 | |
|       z |= 1;
 | |
|     }
 | |
|     res.merge_back(z);
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| BitPfxCollection BitPfxCollection::operator+(const BitPfxCollection& other) const {
 | |
|   if (!other.pfx.size()) {
 | |
|     return *this;
 | |
|   }
 | |
|   if (!pfx.size()) {
 | |
|     return other;
 | |
|   }
 | |
|   BitPfxCollection res;
 | |
|   res.pfx.reserve(pfx.size() + other.pfx.size());
 | |
|   std::size_t i = 0, j = 0, m = pfx.size(), n = other.pfx.size();
 | |
|   struct Interval {
 | |
|     unsigned long long z, a, b;
 | |
|     void operator=(unsigned long long _z) {
 | |
|       z = _z;
 | |
|       a = (_z & (_z - 1));
 | |
|       b = (_z | (_z - 1));
 | |
|     }
 | |
|     bool contains(const Interval& other) const {
 | |
|       return a <= other.a && other.b <= b;
 | |
|     }
 | |
|   };
 | |
|   Interval U, V;
 | |
|   U = pfx[0];
 | |
|   V = other.pfx[0];
 | |
|   while (i < m && j < n) {
 | |
|     if (U.b < V.b || (U.b == V.b && U.a >= V.a)) {
 | |
|       if (U.a < V.a) {
 | |
|         res.merge_back(U.z);
 | |
|       }
 | |
|       if (++i == m) {
 | |
|         break;
 | |
|       }
 | |
|       U = pfx[i];
 | |
|     } else {
 | |
|       if (V.a < U.a) {
 | |
|         res.merge_back(V.z);
 | |
|       }
 | |
|       if (++j == n) {
 | |
|         break;
 | |
|       }
 | |
|       V = other.pfx[j];
 | |
|     }
 | |
|   }
 | |
|   while (i < m) {
 | |
|     res.merge_back(pfx[i++]);
 | |
|   }
 | |
|   while (j < n) {
 | |
|     res.merge_back(other.pfx[j++]);
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| bool BitPfxCollection::operator+=(const BitPfxCollection& other) {
 | |
|   BitPfxCollection tmp = *this + other;
 | |
|   if (*this == tmp) {
 | |
|     return false;
 | |
|   } else {
 | |
|     *this = tmp;
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void AdmissibilityInfo::set_all(bool val) {
 | |
|   dim = 0;
 | |
|   info.clear();
 | |
|   info.resize(1, val);
 | |
| }
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, const AdmissibilityInfo& p) {
 | |
|   p.show(os);
 | |
|   return os;
 | |
| }
 | |
| 
 | |
| void AdmissibilityInfo::show(std::ostream& os) const {
 | |
|   os << '[';
 | |
|   for (bool x : info) {
 | |
|     os << (int)x;
 | |
|   }
 | |
|   os << ']';
 | |
| }
 | |
| 
 | |
| void AdmissibilityInfo::extend(int dim1) {
 | |
|   if (dim < dim1) {
 | |
|     std::size_t i, n = info.size(), n1 = (size_t(1) << (2 * dim1));
 | |
|     assert(n);
 | |
|     info.resize(n1);
 | |
|     for (i = n; i < n1; i++) {
 | |
|       info[i] = info[i - n];
 | |
|     }
 | |
|     dim = dim1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void AdmissibilityInfo::operator|=(const AdmissibilityInfo& other) {
 | |
|   extend(other.dim);
 | |
|   std::size_t i, j, n = info.size(), n1 = other.info.size();
 | |
|   assert(n1 && !(n1 & (n1 - 1)));
 | |
|   for (i = j = 0; i < n; i++) {
 | |
|     info[i] = info[i] | other.info[j];
 | |
|     j = (j + 1) & (n1 - 1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void AdmissibilityInfo::set_by_pattern(int pdim, int pattern[]) {
 | |
|   extend(pdim);
 | |
|   std::size_t n = info.size();
 | |
|   for (std::size_t x = 0; x < n; x++) {
 | |
|     std::size_t y = x;
 | |
|     bool f = true;
 | |
|     for (int i = 0; i < pdim; i++) {
 | |
|       if (!((pattern[i] >> (y & 3)) & 1)) {
 | |
|         f = false;
 | |
|         break;
 | |
|       }
 | |
|       y >>= 2;
 | |
|     }
 | |
|     if (f) {
 | |
|       info[x] = true;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| int AdmissibilityInfo::conflicts_at(const AdmissibilityInfo& other) const {
 | |
|   std::size_t i, n1 = info.size(), n2 = other.info.size(), n = std::max(n1, n2);
 | |
|   for (i = 0; i < n; i++) {
 | |
|     if (info[i & (n1 - 1)] && other.info[i & (n2 - 1)]) {
 | |
|       return (int)i;
 | |
|     }
 | |
|   }
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| bool AdmissibilityInfo::extract1(char A[4], char tag, int p1) const {
 | |
|   char B[4];
 | |
|   std::memset(B, 0, sizeof(B));
 | |
|   p1 <<= 1;
 | |
|   std::size_t n = info.size() - 1;
 | |
|   for (std::size_t x = 0; x <= n; x++) {
 | |
|     if (info[x]) {
 | |
|       B[(x >> p1) & 3] = 1;
 | |
|     }
 | |
|   }
 | |
|   int m1 = ((n >> p1) & 3);
 | |
|   for (int i = 0; i < 4; i++) {
 | |
|     if (B[i & m1]) {
 | |
|       if (A[i] && A[i] != tag) {
 | |
|         A[i] = -1;
 | |
|         return false;
 | |
|       }
 | |
|       A[i] = tag;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool AdmissibilityInfo::extract2(char A[4][4], char tag, int p1, int p2) const {
 | |
|   char B[4][4];
 | |
|   std::memset(B, 0, sizeof(B));
 | |
|   p1 <<= 1;
 | |
|   p2 <<= 1;
 | |
|   std::size_t n = info.size() - 1;
 | |
|   for (std::size_t x = 0; x <= n; x++) {
 | |
|     if (info[x]) {
 | |
|       B[(x >> p1) & 3][(x >> p2) & 3] = 1;
 | |
|     }
 | |
|   }
 | |
|   int m1 = ((n >> p1) & 3);
 | |
|   int m2 = ((n >> p2) & 3);
 | |
|   for (int i = 0; i < 4; i++) {
 | |
|     for (int j = 0; j < 4; j++) {
 | |
|       if (B[i & m1][j & m2]) {
 | |
|         if (A[i][j] && A[i][j] != tag) {
 | |
|           A[i][j] = -1;
 | |
|           return false;
 | |
|         }
 | |
|         A[i][j] = tag;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool AdmissibilityInfo::extract3(char A[4][4][4], char tag, int p1, int p2, int p3) const {
 | |
|   char B[4][4][4];
 | |
|   std::memset(B, 0, sizeof(B));
 | |
|   p1 <<= 1;
 | |
|   p2 <<= 1;
 | |
|   p3 <<= 1;
 | |
|   std::size_t n = info.size() - 1;
 | |
|   for (std::size_t x = 0; x <= n; x++) {
 | |
|     if (info[x]) {
 | |
|       B[(x >> p1) & 3][(x >> p2) & 3][(x >> p3) & 3] = 1;
 | |
|     }
 | |
|   }
 | |
|   int m1 = ((n >> p1) & 3);
 | |
|   int m2 = ((n >> p2) & 3);
 | |
|   int m3 = ((n >> p3) & 3);
 | |
|   for (int i = 0; i < 4; i++) {
 | |
|     for (int j = 0; j < 4; j++) {
 | |
|       for (int k = 0; k < 4; k++) {
 | |
|         if (B[i & m1][j & m2][k & m3]) {
 | |
|           if (A[i][j][k] && A[i][j][k] != tag) {
 | |
|             A[i][j][k] = -1;
 | |
|             return false;
 | |
|           }
 | |
|           A[i][j][k] = tag;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void ConflictGraph::set_clique(ConflictSet set) {
 | |
|   if (set.x) {
 | |
|     for (int i = 0; i < 64; i++) {
 | |
|       if (set[i]) {
 | |
|         g[i] |= set;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, const BinTrie& bt) {
 | |
|   bt.show(os);
 | |
|   return os;
 | |
| }
 | |
| 
 | |
| void BinTrie::show(std::ostream& os, unsigned long long pfx) const {
 | |
|   unsigned long long x = pfx, u = (td::lower_bit64(x) >> 1);
 | |
|   while (x & ((1ULL << 63) - 1)) {
 | |
|     os << (x >> 63);
 | |
|     x <<= 1;
 | |
|   }
 | |
|   os << " t=" << tag << "; dt=" << down_tag << "; ud=" << useful_depth << std::endl;
 | |
|   if (left) {
 | |
|     left->show(os, pfx - u);
 | |
|   }
 | |
|   if (right) {
 | |
|     right->show(os, pfx + u);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void BinTrie::ins_path(unsigned long long path, unsigned long long new_tag) {
 | |
|   if (!path || !new_tag) {
 | |
|     return;
 | |
|   }
 | |
|   if (!(path & ((1ULL << 63) - 1))) {
 | |
|     tag |= new_tag;
 | |
|     return;
 | |
|   } else if ((long long)path >= 0) {
 | |
|     left = insert_path(std::move(left), path << 1, new_tag);
 | |
|   } else {
 | |
|     right = insert_path(std::move(right), path << 1, new_tag);
 | |
|   }
 | |
|   if (left && right) {
 | |
|     tag |= left->tag & right->tag;
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::unique_ptr<BinTrie> BinTrie::insert_path(std::unique_ptr<BinTrie> root, unsigned long long path,
 | |
|                                               unsigned long long tag) {
 | |
|   if (!path || !tag) {
 | |
|     return root;
 | |
|   }
 | |
|   if (root) {
 | |
|     root->ins_path(path, tag);
 | |
|     return root;
 | |
|   }
 | |
|   if (!(path & ((1ULL << 63) - 1))) {
 | |
|     return std::make_unique<BinTrie>(tag);
 | |
|   }
 | |
|   if ((long long)path >= 0) {
 | |
|     return std::make_unique<BinTrie>(0, insert_path({}, path << 1, tag), std::unique_ptr<BinTrie>{});
 | |
|   } else {
 | |
|     return std::make_unique<BinTrie>(0, std::unique_ptr<BinTrie>{}, insert_path({}, path << 1, tag));
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::unique_ptr<BinTrie> BinTrie::insert_paths(std::unique_ptr<BinTrie> root, const BitPfxCollection& paths,
 | |
|                                                unsigned long long tag) {
 | |
|   if (tag) {
 | |
|     for (auto x : paths.pfx) {
 | |
|       root = insert_path(std::move(root), x, tag);
 | |
|     }
 | |
|   }
 | |
|   return root;
 | |
| }
 | |
| 
 | |
| unsigned long long BinTrie::lookup_tag(unsigned long long path) const {
 | |
|   const BinTrie* node = lookup_node_const(path);
 | |
|   return node ? node->tag : 0;
 | |
| }
 | |
| 
 | |
| BinTrie* BinTrie::lookup_node(unsigned long long path) {
 | |
|   if (!path) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   if (!(path & ((1ULL << 63) - 1))) {
 | |
|     return this;
 | |
|   }
 | |
|   if ((long long)path >= 0) {
 | |
|     return left ? left->lookup_node(path << 1) : nullptr;
 | |
|   } else {
 | |
|     return right ? right->lookup_node(path << 1) : nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const BinTrie* BinTrie::lookup_node_const(unsigned long long path) const {
 | |
|   if (!path) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   if (!(path & ((1ULL << 63) - 1))) {
 | |
|     return this;
 | |
|   }
 | |
|   if ((long long)path >= 0) {
 | |
|     return left ? left->lookup_node_const(path << 1) : nullptr;
 | |
|   } else {
 | |
|     return right ? right->lookup_node_const(path << 1) : nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void BinTrie::set_conflict_graph(ConflictGraph& gr, unsigned long long colors) const {
 | |
|   colors |= tag;
 | |
|   if (!left || !right) {
 | |
|     gr.set_clique(ConflictSet{colors});
 | |
|   }
 | |
|   if (left) {
 | |
|     left->set_conflict_graph(gr, colors);
 | |
|   }
 | |
|   if (right) {
 | |
|     right->set_conflict_graph(gr, colors);
 | |
|   }
 | |
| }
 | |
| 
 | |
| int BinTrie::compute_useful_depth(unsigned long long colors) {
 | |
|   int res = 0;  // useless;
 | |
|   down_tag = tag = colors |= tag;
 | |
|   if (left) {
 | |
|     res = left->compute_useful_depth(colors);
 | |
|     down_tag |= left->down_tag;
 | |
|   }
 | |
|   if (right) {
 | |
|     res = std::max(res, right->compute_useful_depth(colors));
 | |
|     down_tag |= right->down_tag;
 | |
|   }
 | |
|   if (res > 0) {
 | |
|     return useful_depth = res + 1;
 | |
|   }
 | |
|   if (left && right && (left->down_tag & ~right->down_tag) != 0 && (right->down_tag & ~left->down_tag) != 0) {
 | |
|     return useful_depth = 1;
 | |
|   }
 | |
|   return useful_depth = 0;
 | |
| }
 | |
| 
 | |
| unsigned long long BinTrie::build_submap(int depth, unsigned long long A[]) const {
 | |
|   if (!depth) {
 | |
|     A[0] = down_tag | (useful_depth ? (1ULL << 63) : 0);
 | |
|     return down_tag != 0;
 | |
|   }
 | |
|   int n = (1 << (depth - 1));
 | |
|   unsigned long long r1 = 0, r2 = 0;
 | |
|   if (left) {
 | |
|     r1 = left->build_submap(depth - 1, A);
 | |
|   } else {
 | |
|     std::memset(A, 0, n * 8);
 | |
|   }
 | |
|   if (right) {
 | |
|     r2 = right->build_submap(depth - 1, A + n);
 | |
|   } else {
 | |
|     std::memset(A + n, 0, n * 8);
 | |
|   }
 | |
|   if (A[n] != A[n - 1] || (long long)A[n] < 0) {
 | |
|     r2 |= 1;
 | |
|   } else {
 | |
|     r2 &= ~1;
 | |
|   }
 | |
|   return r1 | (r2 << n);
 | |
| }
 | |
| 
 | |
| unsigned long long BinTrie::build_submap_at(int depth, unsigned long long A[], unsigned long long pfx) const {
 | |
|   const BinTrie* node = lookup_node_const(pfx);
 | |
|   if (!node) {
 | |
|     std::memset(A, 0, 8 << depth);
 | |
|     return 0;
 | |
|   }
 | |
|   return node->build_submap(depth, A);
 | |
| }
 | |
| 
 | |
| unsigned long long BinTrie::find_conflict_path(unsigned long long colors, unsigned long long mask) const {
 | |
|   colors |= tag & mask;
 | |
|   if (!left && !right) {
 | |
|     return colors & (colors - 1) ? (1ULL << 63) : 0;
 | |
|   }
 | |
|   if (!left) {
 | |
|     if (colors & (colors - 1)) {
 | |
|       return (1ULL << 62);  // $0
 | |
|     } else {
 | |
|       unsigned long long x = right->find_conflict_path(colors, mask);
 | |
|       return x ? ((x >> 1) | (1ULL << 63)) : 0;
 | |
|     }
 | |
|   } else if (!right) {
 | |
|     if (colors & (colors - 1)) {
 | |
|       return (3ULL << 62);  // $1
 | |
|     } else {
 | |
|       return left->find_conflict_path(colors, mask) >> 1;
 | |
|     }
 | |
|   }
 | |
|   unsigned long long x = left->find_conflict_path(colors, mask);
 | |
|   unsigned long long y = right->find_conflict_path(colors, mask);
 | |
|   if (td::lower_bit64(y) > td::lower_bit64(x)) {
 | |
|     return (y >> 1) | (1ULL << 63);
 | |
|   } else {
 | |
|     return x >> 1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, MinMaxSize t) {
 | |
|   t.show(os);
 | |
|   return os;
 | |
| }
 | |
| 
 | |
| void MinMaxSize::normalize() {
 | |
|   if (minmax_size & (0xfff800f8U * 0x100000001ULL)) {
 | |
|     nrm(0xf8, 0x7);
 | |
|     nrm(0xfff80000U, 0x7ff00);
 | |
|     nrm(0xf8ULL << 32, 7ULL << 32);
 | |
|     nrm(0xfff80000ULL << 32, 0x7ff00ULL << 32);
 | |
|   }
 | |
| }
 | |
| 
 | |
| MinMaxSize::unpacked::unpacked(MinMaxSize val) {
 | |
|   val.normalize();
 | |
|   max_refs = val.minmax_size & 0xff;
 | |
|   max_bits = (val.minmax_size >> 8) & 0x7ff;
 | |
|   min_refs = (val.minmax_size >> 32) & 0xff;
 | |
|   min_bits = (val.minmax_size >> 40) & 0x7ff;
 | |
| }
 | |
| 
 | |
| MinMaxSize MinMaxSize::unpacked::pack() const {
 | |
|   unsigned long long t = ((unsigned long long)(min_bits * 0x100 + min_refs) << 32);
 | |
|   t += (max_bits * 0x100 + max_refs);
 | |
|   return MinMaxSize{t};
 | |
| }
 | |
| 
 | |
| MinMaxSize& MinMaxSize::repeat(int count) {
 | |
|   if (count <= 0) {
 | |
|     return clear();
 | |
|   }
 | |
|   if (count == 1) {
 | |
|     return *this;
 | |
|   }
 | |
|   unpacked z{*this};
 | |
|   count = std::min(count, 1024);
 | |
|   z.max_refs = std::min(z.max_refs * count, 7U);
 | |
|   z.max_bits = std::min(z.max_bits * count, 0x7ffU);
 | |
|   z.min_refs = std::min(z.min_refs * count, 7U);
 | |
|   z.min_bits = std::min(z.min_bits * count, 0x7ffU);
 | |
|   return *this = z.pack();
 | |
| }
 | |
| 
 | |
| MinMaxSize& MinMaxSize::repeat_at_least(int count) {
 | |
|   count = std::min(std::max(count, 0), 1024);
 | |
|   unpacked z{*this};
 | |
|   if (z.max_refs) {
 | |
|     z.max_refs = 7;
 | |
|   }
 | |
|   if (z.max_bits) {
 | |
|     z.max_bits = 0x7ff;
 | |
|   }
 | |
|   z.min_refs = std::min(z.min_refs * count, 7U);
 | |
|   z.min_bits = std::min(z.min_bits * count, 0x7ffU);
 | |
|   return *this = z.pack();
 | |
| }
 | |
| 
 | |
| MinMaxSize& MinMaxSize::operator|=(MinMaxSize y) {
 | |
|   unpacked z{*this}, w{y};
 | |
|   z.min_refs = std::min(z.min_refs, w.min_refs);
 | |
|   z.min_bits = std::min(z.min_bits, w.min_bits);
 | |
|   z.max_refs = std::max(z.max_refs, w.max_refs);
 | |
|   z.max_bits = std::max(z.max_bits, w.max_bits);
 | |
|   return *this = z.pack();
 | |
| }
 | |
| 
 | |
| void MinMaxSize::show(std::ostream& os) const {
 | |
|   unpacked z{*this};
 | |
|   z.show(os);
 | |
| }
 | |
| 
 | |
| void MinMaxSize::unpacked::show(std::ostream& os) const {
 | |
|   bool fixed = (min_bits == max_bits && min_refs == max_refs);
 | |
|   if (fixed) {
 | |
|     os << '=';
 | |
|   }
 | |
|   if (min_bits >= 1024 && min_refs >= 7) {
 | |
|     os << "infty";
 | |
|   } else {
 | |
|     os << min_bits;
 | |
|     if (min_refs) {
 | |
|       os << "+" << min_refs << "R";
 | |
|     }
 | |
|   }
 | |
|   if (!fixed) {
 | |
|     os << "..";
 | |
|     if (max_bits >= 1024 && max_refs >= 7) {
 | |
|       os << "infty";
 | |
|     } else {
 | |
|       os << max_bits;
 | |
|       if (max_refs) {
 | |
|         os << "+" << max_refs << "R";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace tlbc
 | |
| 
 | |
| namespace tlbc {
 | |
| 
 | |
| using src::Lexem;
 | |
| using src::Lexer;
 | |
| using sym::sym_idx_t;
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *  DATA TYPES: Type Expressions, Types, Constructors
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| // headers in tlbc-data.h
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, const TypeExpr* te) {
 | |
|   if (te) {
 | |
|     te->show(os);
 | |
|   } else {
 | |
|     os << "(null-type)";
 | |
|   }
 | |
|   return os;
 | |
| }
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, const Constructor& cs) {
 | |
|   cs.show(os);
 | |
|   return os;
 | |
| }
 | |
| 
 | |
| TypeExpr type_Type{{}, TypeExpr::te_Type};
 | |
| 
 | |
| TypeExpr* const_type_expr[TypeExpr::max_const_expr];
 | |
| int const_type_expr_num;
 | |
| 
 | |
| TypeExpr* TypeExpr::const_htable[TypeExpr::const_htable_size];
 | |
| 
 | |
| sym_idx_t Nat_name, Eq_name, Less_name, Leq_name;
 | |
| Type* Nat_type;
 | |
| Type *Eq_type, *Less_type, *Leq_type;
 | |
| Type *NatWidth_type, *NatLess_type, *NatLeq_type, *Int_type, *UInt_type;
 | |
| Type* Bits_type;
 | |
| Type *Any_type, *Cell_type;
 | |
| 
 | |
| int types_num, builtin_types_num;
 | |
| std::vector<Type> types;
 | |
| 
 | |
| int Type::last_declared_counter;
 | |
| 
 | |
| void TypeExpr::check_mode(const src::SrcLocation& loc, int mode) {
 | |
|   if (!(mode & (1 << (is_nat ? 1 : 0)))) {
 | |
|     if (is_nat) {
 | |
|       throw src::ParseError{loc, "type expression required"};
 | |
|     } else {
 | |
|       throw src::ParseError{loc, "integer expression required"};
 | |
|     }
 | |
|   }
 | |
|   if (tchk_only && !(mode & 8)) {
 | |
|     throw src::ParseError{where, "type expression can be used only in a type-checking context"};
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool TypeExpr::no_tchk() const {
 | |
|   if (tchk_only) {
 | |
|     throw src::ParseError{where, "type expression can be used only in a type-checking context"};
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_intconst(const src::SrcLocation& loc, unsigned int_const) {
 | |
|   return new (AR) TypeExpr{loc, te_IntConst, (int)int_const};
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_intconst(const src::SrcLocation& loc, std::string int_const) {
 | |
|   char* end_ptr = 0;
 | |
|   long long value = -1;
 | |
|   if (!int_const.empty()) {
 | |
|     value = std::strtoll(int_const.c_str(), &end_ptr, 0);
 | |
|   }
 | |
|   if (value < 0 || value >= (1LL << 31) || end_ptr != int_const.c_str() + int_const.size()) {
 | |
|     throw src::ParseError{loc, "integer constant does not fit in an unsigned 31-bit integer"};
 | |
|   }
 | |
|   return mk_intconst(loc, (unsigned)value);
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_apply_gen(const src::SrcLocation& loc, TypeExpr* expr1, TypeExpr* expr2) {
 | |
|   if (expr1->tp != te_Apply) {
 | |
|     throw src::ParseError{loc, "cannot apply one expression to the other"};
 | |
|   }
 | |
|   expr1->args.push_back(expr2);
 | |
|   return expr1;
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_mulint(const src::SrcLocation& loc, TypeExpr* expr1, TypeExpr* expr2) {
 | |
|   if (expr1->tp != te_IntConst && expr2->tp != te_IntConst) {
 | |
|     throw src::ParseError{loc, "multiplication allowed only by constant values"};
 | |
|   }
 | |
|   if (expr2->tp != te_IntConst) {
 | |
|     std::swap(expr1, expr2);
 | |
|   }
 | |
|   if (!expr1->is_nat) {
 | |
|     throw src::ParseError{expr1->where, "argument to integer multiplication should be a number"};
 | |
|   }
 | |
|   if (expr1->tp == te_IntConst) {
 | |
|     long long product = (long long)expr1->value * expr2->value;
 | |
|     if (product < 0 || product >= (1LL << 31)) {
 | |
|       throw src::ParseError{loc, "product does not git in 31 bits"};
 | |
|     }
 | |
|     return mk_intconst(loc, (unsigned)product);
 | |
|   }
 | |
|   int val = expr2->value;
 | |
|   if (!val) {
 | |
|     return expr2;
 | |
|   }
 | |
|   // delete expr2;
 | |
|   return new (AR) TypeExpr{loc, te_MulConst, val, {expr1}, expr1->negated};
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_apply(const src::SrcLocation& loc, int tp, TypeExpr* expr1, TypeExpr* expr2) {
 | |
|   TypeExpr* expr = new (AR) TypeExpr{loc, tp, 0, {expr1, expr2}};
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_cellref(const src::SrcLocation& loc, TypeExpr* expr1) {
 | |
|   TypeExpr* expr = new (AR) TypeExpr{loc, te_Ref, 0, {expr1}};
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| Field& Constructor::new_field(const src::SrcLocation& field_where, bool implicit, sym_idx_t name = 0) {
 | |
|   assert((long)fields_num == (long)fields.size());
 | |
|   if (name) {
 | |
|     sym::SymDef* sym_def = sym::lookup_symbol(name, 1);
 | |
|     if (sym_def) {
 | |
|       throw src::ParseError{field_where, "redefined field or parameter"};
 | |
|     }
 | |
|   }
 | |
|   fields.emplace_back(field_where, implicit, fields_num++, name);
 | |
|   return fields.back();
 | |
| }
 | |
| 
 | |
| std::string Field::get_name() const {
 | |
|   return sym::symbols.get_name(name);
 | |
| }
 | |
| 
 | |
| // register symbol in local symbol table
 | |
| void Field::register_sym() const {
 | |
|   if (name) {
 | |
|     sym::SymDef* sym_def = sym::lookup_symbol(name, 1);
 | |
|     if (sym_def) {
 | |
|       throw src::ParseError{loc, "redefined field or parameter"};
 | |
|     } else {
 | |
|       sym_def = sym::define_symbol(name, true, loc);
 | |
|       if (!sym_def) {
 | |
|         throw src::ParseError{loc, "cannot register field"};
 | |
|       }
 | |
|     }
 | |
|     delete sym_def->value;
 | |
|     sym_def->value = new SymVal((int)SymVal::_Param, field_idx, type);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool TypeExpr::close(const src::SrcLocation& loc) {
 | |
|   if (tp != te_Apply) {
 | |
|     return true;
 | |
|   }
 | |
|   Type* type = type_applied;
 | |
|   assert(type);
 | |
|   if (type->arity < 0) {
 | |
|     type->arity = (int)args.size();
 | |
|     type->args.resize(type->arity, 0);
 | |
|   } else if (type->arity != (int)args.size()) {
 | |
|     throw src::ParseError{where,
 | |
|                           std::string{"operator `"} + sym::symbols.get_name(type->type_name) +
 | |
|                               "` applied with incorrect number of arguments, partial type applications not supported"};
 | |
|     return false;
 | |
|   }
 | |
|   is_nat_subtype = type->produces_nat;
 | |
|   bool is_eq = (type == Eq_type);
 | |
|   int neg_cnt = 0;
 | |
|   for (int i = 0; i < type->arity; i++) {
 | |
|     TypeExpr* arg = args[i];
 | |
|     int& x = type->args[i];
 | |
|     if (arg->negated) {
 | |
|       ++neg_cnt;
 | |
|       negated = true;
 | |
|       if (!is_eq) {
 | |
|         if (x & Type::_IsPos) {
 | |
|           throw src::ParseError{arg->where, std::string{"passed an argument of incorrect polarity to `"} +
 | |
|                                                 sym::symbols.get_name(type->type_name) + "`"};
 | |
|         }
 | |
|         x |= Type::_IsNeg;
 | |
|       } else if (neg_cnt == 2) {
 | |
|         throw src::ParseError{loc, "cannot equate two expressions of negative polarity"};
 | |
|       }
 | |
|     }
 | |
|     arg->no_tchk();
 | |
|     if (arg->is_nat) {
 | |
|       x |= Type::_IsNat;
 | |
|     } else {
 | |
|       x |= Type::_IsType;
 | |
|       if (arg->negated) {
 | |
|         throw src::ParseError{arg->where, "cannot use negative types as arguments to other types"};
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   tchk_only = negated = neg_cnt;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| TypeExpr* TypeExpr::mk_apply_empty(const src::SrcLocation& loc, sym_idx_t name, Type* type_applied) {
 | |
|   TypeExpr* expr = new (AR) TypeExpr{loc, te_Apply, name};
 | |
|   expr->type_applied = type_applied;
 | |
|   expr->is_nat_subtype = (type_applied->produces_nat && !type_applied->arity);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| void Type::print_name(std::ostream& os) const {
 | |
|   if (type_name) {
 | |
|     os << sym::symbols.get_name(type_name);
 | |
|   } else {
 | |
|     os << "TYPE_" << type_idx;
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::string Type::get_name() const {
 | |
|   if (type_name) {
 | |
|     return sym::symbols.get_name(type_name);
 | |
|   } else {
 | |
|     std::ostringstream os;
 | |
|     os << "TYPE_" << type_idx;
 | |
|     return os.str();
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::string Constructor::get_name() const {
 | |
|   return sym::symbols.get_name(constr_name);
 | |
| }
 | |
| 
 | |
| std::string Constructor::get_qualified_name() const {
 | |
|   return type_defined->get_name() + "::" + get_name();
 | |
| }
 | |
| 
 | |
| void TypeExpr::show(std::ostream& os, const Constructor* cs, int prio, int mode) const {
 | |
|   if (mode & 2) {
 | |
|     prio = 0;
 | |
|   }
 | |
|   switch (tp) {
 | |
|     case te_Type:
 | |
|       os << "Type";
 | |
|       break;
 | |
|     case te_Param: {
 | |
|       int i = value;
 | |
|       sym_idx_t param_name = 0;
 | |
|       if (cs && i >= 0 && i < cs->fields_num) {
 | |
|         param_name = cs->fields.at(i).name;
 | |
|       }
 | |
|       if (negated ^ (mode & 1)) {
 | |
|         os << '~';
 | |
|       }
 | |
|       if (param_name > 0) {
 | |
|         os << sym::symbols.get_name(param_name);
 | |
|       } else {
 | |
|         os << '_' << i + 1;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case te_Apply:
 | |
|       if (!args.size() && !type_applied->type_name && type_applied->constr_num == 1 &&
 | |
|           !type_applied->constructors.at(0)->constr_name &&
 | |
|           !(type_applied->constructors[0]->tag & ((1ULL << 63) - 1))) {
 | |
|         type_applied->constructors[0]->show(os, mode | 4);
 | |
|       } else {
 | |
|         if (prio > 90 && args.size()) {
 | |
|           os << '(';
 | |
|         }
 | |
|         type_applied->print_name(os);
 | |
|         for (TypeExpr* arg : args) {
 | |
|           os << ' ';  // priority 90
 | |
|           arg->show(os, cs, 91, mode);
 | |
|         }
 | |
|         if (prio > 90 && args.size()) {
 | |
|           os << ')';
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
|     case te_Add: {
 | |
|       assert(args.size() == 2);
 | |
|       if (prio > 20) {
 | |
|         os << '(';
 | |
|       }
 | |
|       args[0]->show(os, cs, 20, mode);
 | |
|       os << " + ";  // priority 20
 | |
|       args[1]->show(os, cs, 21, mode);
 | |
|       if (prio > 20) {
 | |
|         os << ')';
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case te_GetBit: {
 | |
|       assert(args.size() == 2);
 | |
|       if (prio > 97) {
 | |
|         os << '(';
 | |
|       }
 | |
|       args[0]->show(os, cs, 98, mode);
 | |
|       os << ".";  // priority 20
 | |
|       args[1]->show(os, cs, 98, mode);
 | |
|       if (prio > 97) {
 | |
|         os << ')';
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case te_IntConst: {
 | |
|       assert(args.empty());
 | |
|       os << value;
 | |
|       break;
 | |
|     }
 | |
|     case te_MulConst: {
 | |
|       if (prio > 30) {
 | |
|         os << '(';
 | |
|       }
 | |
|       assert(args.size() == 1);
 | |
|       os << value << " * ";  // priority 30
 | |
|       args[0]->show(os, cs, 31, mode);
 | |
|       if (prio > 30) {
 | |
|         os << ')';
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case te_Tuple: {
 | |
|       assert(args.size() == 2);
 | |
|       if (prio > 30) {
 | |
|         os << '(';
 | |
|       }
 | |
|       args[0]->show(os, cs, 30, mode);
 | |
|       os << " * ";  // priority 30
 | |
|       args[1]->show(os, cs, 31, mode);
 | |
|       if (prio > 30) {
 | |
|         os << ')';
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case te_CondType: {
 | |
|       assert(args.size() == 2);
 | |
|       if (prio > 95) {
 | |
|         os << '(';
 | |
|       }
 | |
|       args[0]->show(os, cs, 96, mode);
 | |
|       os << "?";  // priority 95
 | |
|       args[1]->show(os, cs, 96, mode);
 | |
|       if (prio > 95) {
 | |
|         os << ')';
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case te_Ref: {
 | |
|       assert(args.size() == 1);
 | |
|       os << '^';  // priority 100
 | |
|       args[0]->show(os, cs, 100, mode);
 | |
|       break;
 | |
|     }
 | |
|     default:
 | |
|       os << "(unknown-type)";
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool TypeExpr::equal(const TypeExpr& other) const {
 | |
|   if (tp != other.tp || value != other.value || type_applied != other.type_applied ||
 | |
|       args.size() != other.args.size()) {
 | |
|     return false;
 | |
|   }
 | |
|   for (std::size_t i = 0; i < args.size(); i++) {
 | |
|     if (!args[i]->equal(*other.args[i])) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // 0 = 0, 1 = 1, 2 = any even >= 2, 3 = any odd >= 3
 | |
| // (We work in N/(4~2), or in the free semilattice generated by it.)
 | |
| int abstract_nat_const(int value) {
 | |
|   return 1 << ((value & 1) + (value >= 2 ? 2 : 0));
 | |
| }
 | |
| 
 | |
| unsigned char abstract_add_base_table[4][4] = {{0, 1, 2, 3}, {1, 2, 3, 2}, {2, 3, 2, 3}, {3, 2, 3, 2}};
 | |
| unsigned char abstract_mul_base_table[4][4] = {{0, 0, 0, 0}, {0, 1, 2, 3}, {0, 2, 2, 2}, {0, 3, 2, 3}};
 | |
| unsigned char abstract_getbit_b_table[4][4] = {{1, 1, 1, 1}, {2, 1, 1, 1}, {1, 3, 3, 3}, {2, 3, 3, 3}};
 | |
| 
 | |
| unsigned char abstract_add_table[16][16];
 | |
| unsigned char abstract_mul_table[16][16];
 | |
| unsigned char abstract_getbit_table[16][16];
 | |
| 
 | |
| void compute_semilat_table(unsigned char table[16][16], const unsigned char base_table[4][4]) {
 | |
|   for (int x = 0; x < 16; x++) {
 | |
|     for (int y = 0; y < 16; y++) {
 | |
|       int res = 0;
 | |
|       for (int i = 0; i < 4; i++) {
 | |
|         if ((x >> i) & 1) {
 | |
|           for (int j = 0; j < 4; j++) {
 | |
|             if ((y >> j) & 1) {
 | |
|               res |= 1 << base_table[i][j];
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       table[x][y] = (unsigned char)res;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void compute_semilat_b_table(unsigned char table[16][16], const unsigned char b_table[4][4]) {
 | |
|   for (int x = 0; x < 16; x++) {
 | |
|     for (int y = 0; y < 16; y++) {
 | |
|       int res = 0;
 | |
|       for (int i = 0; i < 4; i++) {
 | |
|         if ((x >> i) & 1) {
 | |
|           for (int j = 0; j < 4; j++) {
 | |
|             if ((y >> j) & 1) {
 | |
|               res |= b_table[i][j];
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       table[x][y] = (unsigned char)res;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void init_abstract_tables() {
 | |
|   compute_semilat_table(abstract_add_table, abstract_add_base_table);
 | |
|   compute_semilat_table(abstract_mul_table, abstract_mul_base_table);
 | |
|   compute_semilat_b_table(abstract_getbit_table, abstract_getbit_b_table);
 | |
| }
 | |
| 
 | |
| int abstract_add(int x, int y) {
 | |
|   return abstract_add_table[x & 15][y & 15];
 | |
| }
 | |
| 
 | |
| int abstract_mul(int x, int y) {
 | |
|   return abstract_mul_table[x & 15][y & 15];
 | |
| }
 | |
| 
 | |
| int abstract_getbit(int x, int y) {
 | |
|   return abstract_getbit_table[x & 15][y & 15];
 | |
| }
 | |
| 
 | |
| int TypeExpr::abstract_interpret_nat() const {
 | |
|   if (!is_nat || tchk_only) {
 | |
|     return 0;
 | |
|   }
 | |
|   switch (tp) {
 | |
|     case te_Param:
 | |
|       return 0xf;  // for now, natural parameters can take arbitrary values
 | |
|     case te_Add:
 | |
|       assert(args.size() == 2);
 | |
|       return abstract_add(args[0]->abstract_interpret_nat(), args[1]->abstract_interpret_nat());
 | |
|     case te_GetBit:
 | |
|       assert(args.size() == 2);
 | |
|       return abstract_getbit(args[0]->abstract_interpret_nat(), args[1]->abstract_interpret_nat());
 | |
|     case te_IntConst:
 | |
|       return abstract_nat_const(value);
 | |
|     case te_MulConst:
 | |
|       assert(args.size() == 1);
 | |
|       return abstract_mul(args[0]->abstract_interpret_nat(), abstract_nat_const(value));
 | |
|     default:
 | |
|       return 0xf;
 | |
|   }
 | |
| }
 | |
| 
 | |
| MinMaxSize TypeExpr::compute_size() const {
 | |
|   if (is_nat) {
 | |
|     return {0};
 | |
|   }
 | |
|   switch (tp) {
 | |
|     case te_Type:
 | |
|       return {0};
 | |
|     case te_Param:
 | |
|       return {MinMaxSize::Any};  // any size possible for type parameters
 | |
|     case te_Ref: {
 | |
|       assert(args.size() == 1);
 | |
|       bool f = args[0]->compute_size().is_possible();
 | |
|       return f ? MinMaxSize::OneRef : MinMaxSize::Impossible;
 | |
|     }
 | |
|     case te_CondType: {
 | |
|       assert(args.size() == 2);
 | |
|       int z = args[0]->abstract_interpret_nat();
 | |
|       if (!(z & ~1)) {
 | |
|         return {0};  // always 0
 | |
|       } else {
 | |
|         MinMaxSize t = args[1]->compute_size();
 | |
|         if (z & 1) {
 | |
|           t.clear_min();
 | |
|         }
 | |
|         return t;
 | |
|       }
 | |
|     }
 | |
|     case te_Tuple: {
 | |
|       assert(args.size() == 2);
 | |
|       int z = args[0]->abstract_interpret_nat();
 | |
|       if (!(z & ~1)) {
 | |
|         return {0};  // always 0
 | |
|       } else {
 | |
|         MinMaxSize t = args[1]->compute_size();
 | |
|         if (args[0]->tp == te_IntConst) {
 | |
|           t.repeat(args[0]->value);
 | |
|           return t;
 | |
|         }
 | |
|         if (z & 1) {
 | |
|           t.clear_min();  // zero repetition count possible
 | |
|         }
 | |
|         if (z & 12) {
 | |
|           // may be repeated more than once
 | |
|           int n = ((z & 1) ? 0 : ((z & 2) ? 1 : 2));  // minimal value of repetition count
 | |
|           t.repeat_at_least(n);                       // repetition count >= n
 | |
|         }
 | |
|         return t;
 | |
|       }
 | |
|     }
 | |
|     case te_Apply: {
 | |
|       if (args.size() == 1 && args[0]->tp == te_IntConst) {
 | |
|         int n = args[0]->value;
 | |
|         if (type_applied == NatWidth_type || type_applied == Int_type || type_applied == UInt_type ||
 | |
|             type_applied == Bits_type) {
 | |
|           return MinMaxSize::fixed_size(std::min(n, 2047));
 | |
|         } else if (type_applied == NatLeq_type) {
 | |
|           return MinMaxSize::fixed_size(32 - td::count_leading_zeroes32(n));
 | |
|         } else if (type_applied == NatLess_type) {
 | |
|           return MinMaxSize::fixed_size(n ? 32 - td::count_leading_zeroes32(n - 1) : 2047);
 | |
|         }
 | |
|       }
 | |
|       return type_applied->size;
 | |
|     }
 | |
|   }  // end switch
 | |
|   return {};
 | |
| }
 | |
| 
 | |
| bool TypeExpr::compute_any_bits() const {
 | |
|   if (is_nat) {
 | |
|     return true;
 | |
|   }
 | |
|   switch (tp) {
 | |
|     case te_Type:
 | |
|       return true;
 | |
|     case te_Param:
 | |
|       return false;
 | |
|     case te_Ref:
 | |
|       return true;
 | |
|     case te_Tuple:
 | |
|     case te_CondType: {
 | |
|       assert(args.size() == 2);
 | |
|       int z = args[0]->abstract_interpret_nat();
 | |
|       if (!(z & ~1)) {
 | |
|         return true;  // always 0
 | |
|       } else {
 | |
|         return args[1]->compute_any_bits();
 | |
|       }
 | |
|     }
 | |
|     case te_Apply: {
 | |
|       if (args.size() == 1 && args[0]->tp == te_IntConst) {
 | |
|         int n = args[0]->value;
 | |
|         if (type_applied == NatLeq_type) {
 | |
|           return !(n & (n + 1));
 | |
|         } else if (type_applied == NatLess_type) {
 | |
|           return !(n & (n - 1));
 | |
|         }
 | |
|       }
 | |
|       return type_applied->any_bits;
 | |
|     }
 | |
|   }  // end switch
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| int TypeExpr::is_integer() const {
 | |
|   if (is_nat) {
 | |
|     return 1;
 | |
|   }
 | |
|   if (tp != te_Apply) {
 | |
|     return 0;
 | |
|   }
 | |
|   const Type* ta = type_applied;
 | |
|   if (ta == Int_type) {
 | |
|     return -1;
 | |
|   } else if (ta == UInt_type) {
 | |
|     return 1;
 | |
|   }
 | |
|   if (ta->is_bool) {
 | |
|     return 1;
 | |
|   }
 | |
|   if (ta->is_builtin) {
 | |
|     return ta->is_integer;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| bool TypeExpr::is_ref_to_anon() const {
 | |
|   return tp == te_Ref && args.at(0)->is_anon();
 | |
| }
 | |
| 
 | |
| bool TypeExpr::is_anon() const {
 | |
|   return tp == te_Apply && args.empty() && type_applied->is_anon;
 | |
| }
 | |
| 
 | |
| unsigned long long TypeExpr::compute_hash() const {
 | |
|   unsigned long long h = tp * 17239ULL + value * 23917ULL + 1;
 | |
|   if (type_applied) {
 | |
|     h += 239017 * type_applied->type_idx;
 | |
|   }
 | |
|   for (const TypeExpr* arg : args) {
 | |
|     h *= 170239;
 | |
|     if (!arg->negated) {
 | |
|       h += arg->is_constexpr;
 | |
|     }
 | |
|   }
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| bool TypeExpr::detect_constexpr() {
 | |
|   if (is_constexpr) {
 | |
|     return true;
 | |
|   }
 | |
|   bool c = !negated;
 | |
|   for (TypeExpr* arg : args) {
 | |
|     if (!arg->detect_constexpr() && !arg->negated) {
 | |
|       c = false;
 | |
|     }
 | |
|   }
 | |
|   if (!c || tp == te_Param) {
 | |
|     return false;
 | |
|   }
 | |
|   unsigned long long hash = compute_hash();
 | |
|   unsigned long long h1 = hash % const_htable_size, h2 = 1 + hash % (const_htable_size + 1);
 | |
|   while (const_htable[h1]) {
 | |
|     TypeExpr* other = const_htable[h1];
 | |
|     if (other->tp == tp && other->value == value && other->type_applied == type_applied &&
 | |
|         other->args.size() == args.size()) {
 | |
|       bool match = true;
 | |
|       for (std::size_t i = 0; i < args.size(); i++) {
 | |
|         if (other->args[i]->negated != args[i]->negated || other->args[i]->is_constexpr != args[i]->is_constexpr) {
 | |
|           match = false;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       if (match) {
 | |
|         is_constexpr = other->is_constexpr;
 | |
|         assert(is_constexpr);
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|     h1 += h2;
 | |
|     if (h1 >= const_htable_size) {
 | |
|       h1 -= const_htable_size;
 | |
|     }
 | |
|   }
 | |
|   assert(const_type_expr_num < max_const_expr - 1);
 | |
|   const_type_expr[is_constexpr = ++const_type_expr_num] = this;
 | |
|   const_htable[h1] = this;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void TypeExpr::const_type_name(std::ostream& os) const {
 | |
|   if (negated) {
 | |
|     return;
 | |
|   }
 | |
|   switch (tp) {
 | |
|     case te_Type:
 | |
|       os << "_Type";
 | |
|       return;
 | |
|     case te_Param:
 | |
|       return;
 | |
|     case te_Add:
 | |
|       args[0]->const_type_name(os);
 | |
|       os << "_plus";
 | |
|       args[1]->const_type_name(os);
 | |
|       return;
 | |
|     case te_GetBit:
 | |
|       args[0]->const_type_name(os);
 | |
|       os << "_bit";
 | |
|       args[1]->const_type_name(os);
 | |
|       return;
 | |
|     case te_IntConst:
 | |
|       os << "_" << value;
 | |
|       return;
 | |
|     case te_MulConst:
 | |
|       os << "_mul" << value;
 | |
|       return;
 | |
|     case te_Ref:
 | |
|       os << "_Ref";
 | |
|       args[0]->const_type_name(os);
 | |
|       return;
 | |
|     case te_Tuple:
 | |
|       os << "_tuple";
 | |
|       args[0]->const_type_name(os);
 | |
|       args[1]->const_type_name(os);
 | |
|       return;
 | |
|     case te_CondType:
 | |
|       os << "_if";
 | |
|       args[0]->const_type_name(os);
 | |
|       args[1]->const_type_name(os);
 | |
|       return;
 | |
|     case te_Apply:
 | |
|       os << '_';
 | |
|       if (type_applied->produces_nat) {
 | |
|         if (type_applied == Nat_type) {
 | |
|           os << "nat";
 | |
|         } else if (type_applied == NatWidth_type) {
 | |
|           os << "natwidth";
 | |
|         } else if (type_applied == NatLeq_type) {
 | |
|           os << "natleq";
 | |
|         } else if (type_applied == NatLess_type) {
 | |
|           os << "natless";
 | |
|         }
 | |
|       } else {
 | |
|         os << type_applied->get_name();
 | |
|       }
 | |
|       for (const TypeExpr* arg : args) {
 | |
|         arg->const_type_name(os);
 | |
|       }
 | |
|       return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool TypeExpr::bind_value(bool value_negated, Constructor& cs, bool checking_type) {
 | |
|   // if checking_type == false:
 | |
|   //   negated = false, value_negated = false: compute expression, compare to value (only for integers)
 | |
|   //   negated = false, value_negated = true: compute expression, return it to the "value"
 | |
|   //   negated = true, value_negated = false: assign the value to the expression to compute some of the variables present in the expression
 | |
|   // if checking_type == true:
 | |
|   //   value_negated must be false
 | |
|   /* (debug output)
 | |
|   std::cerr << "binding " << (value_negated ? "negative" : "positive") << " value to expression " << (checking_type ? "of type " : ""); 
 | |
|   show(std::cerr, &cs); 
 | |
|   std::cerr << std::endl;
 | |
|   */
 | |
|   if (!checking_type) {
 | |
|     no_tchk();
 | |
|   } else {
 | |
|     if (is_nat) {
 | |
|       throw src::ParseError{where, "cannot use check a type against an integer expression"};
 | |
|     }
 | |
|     if (value_negated) {
 | |
|       throw src::ParseError{where, "cannot compute a value knowing only its type"};
 | |
|     }
 | |
|   }
 | |
|   if (negated && value_negated) {
 | |
|     // both the expression and the value cannot be negative
 | |
|     throw src::ParseError{where, "expression has wrong polarity"};
 | |
|   }
 | |
|   if (!is_nat) {
 | |
|     // for type expressions:
 | |
|     if (value_negated) {
 | |
|       // cannot "return" values that are not integer (i.e. types)
 | |
|       // throw src::ParseError{where, "cannot assign or return type expressions"};
 | |
|       // in reality, this check should be only when parsing type parameters after constructors
 | |
|     }
 | |
|     if (!checking_type) {
 | |
|       // "true" equality/assignment of type expressions
 | |
|       if (!negated && !value_negated) {
 | |
|         if (tp == te_Apply && args.empty()) {
 | |
|           throw src::ParseError{where, "use of a global type or an undeclared variable"};
 | |
|         } else {
 | |
|           throw src::ParseError{where, "cannot check type expressions for equality"};
 | |
|         }
 | |
|       }
 | |
|       // available only if the expression is a free variable
 | |
|       if (negated && tp != te_Param) {
 | |
|         throw src::ParseError{where, "types can be assigned only to free type variables"};
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   switch (tp) {
 | |
|     case te_Add: {
 | |
|       assert(is_nat && args.size() == 2 && !(args[0]->negated && args[1]->negated));
 | |
|       assert(negated == (args[0]->negated || args[1]->negated));
 | |
|       int i = args[0]->negated;
 | |
|       args[i]->bind_value(negated, cs);
 | |
|       args[1 - i]->bind_value(false, cs);
 | |
|       return true;
 | |
|     }
 | |
|     case te_IntConst: {
 | |
|       assert(is_nat && !negated);
 | |
|       return true;
 | |
|     }
 | |
|     case te_MulConst: {
 | |
|       assert(is_nat && args.size() == 1 && value > 0);
 | |
|       assert(negated == args[0]->negated);
 | |
|       args[0]->bind_value(value_negated, cs);
 | |
|       return true;
 | |
|     }
 | |
|     case te_GetBit: {
 | |
|       assert(is_nat && args.size() == 2 && !args[0]->negated && !args[1]->negated);
 | |
|       assert(!negated);
 | |
|       args[0]->bind_value(false, cs);
 | |
|       args[1]->bind_value(false, cs);
 | |
|       return true;
 | |
|     }
 | |
|     case te_Type: {
 | |
|       assert(!is_nat && !negated);
 | |
|       return true;
 | |
|     }
 | |
|     case te_Param: {
 | |
|       assert(value >= 0 && value < cs.fields_num);
 | |
|       Field& field = cs.fields.at(value);
 | |
|       if (!negated || checking_type) {
 | |
|         if (!field.known) {
 | |
|           throw src::ParseError{where,
 | |
|                                 std::string{"variable `"} + field.get_name() + "` used before being assigned to"};
 | |
|         } else {
 | |
|           field.used = true;
 | |
|         }
 | |
|       } else if (!field.known) {
 | |
|         field.known = true;
 | |
|         // where.show_note(std::string{"variable `"} + field.get_name() + "` is assigned a value here");
 | |
|       }
 | |
|       return true;
 | |
|     }
 | |
|     case te_Apply:
 | |
|       if (type_applied == Eq_type) {
 | |
|         assert(args.size() == 2 && !(args[0]->negated && args[1]->negated));
 | |
|         assert(negated == (args[0]->negated || args[1]->negated));
 | |
|         int i = args[0]->negated;
 | |
|         args[i]->bind_value(negated, cs);
 | |
|         args[1 - i]->bind_value(false, cs);
 | |
|         return true;
 | |
|       } else {
 | |
|         assert(!negated || checking_type);
 | |
|         for (TypeExpr* arg : args) {
 | |
|           if (!arg->negated) {
 | |
|             arg->bind_value(true, cs);
 | |
|           }
 | |
|         }
 | |
|         for (TypeExpr* arg : args) {
 | |
|           if (arg->negated) {
 | |
|             arg->bind_value(false, cs);
 | |
|           }
 | |
|         }
 | |
|         return true;
 | |
|       }
 | |
|     case te_CondType:
 | |
|     case te_Tuple: {
 | |
|       assert(args.size() == 2);
 | |
|       assert(!negated && !args[0]->negated && !args[1]->negated);
 | |
|       assert(args[0]->is_nat);
 | |
|       assert(!args[1]->is_nat);
 | |
|       args[0]->bind_value(true, cs);
 | |
|       args[1]->bind_value(true, cs);
 | |
|       return true;
 | |
|     }
 | |
|     case te_Ref: {
 | |
|       assert(args.size() == 1);
 | |
|       return args[0]->bind_value(value_negated, cs, checking_type);
 | |
|     }
 | |
|     default:
 | |
|       throw src::ParseError{where, "cannot bind a value to an expression of unknown sort"};
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void parse_implicit_param(Lexer& lex, Constructor& cs) {
 | |
|   // ident : # or ident : Type
 | |
|   if (lex.tp() != src::_Ident) {
 | |
|     lex.expect(src::_Ident);
 | |
|   }
 | |
|   Field& field = cs.new_field(lex.cur().loc, true, lex.cur().val);
 | |
|   lex.next();
 | |
|   lex.expect(':');
 | |
|   if (lex.tp() != src::_Type && (lex.tp() != src::_Ident || lex.cur().val != Nat_name)) {
 | |
|     throw src::ParseError{lex.cur().loc, "either `Type` or `#` implicit parameter type expected"};
 | |
|   }
 | |
|   if (lex.tp() == src::_Type) {
 | |
|     field.type = &type_Type;
 | |
|   } else {
 | |
|     field.type = TypeExpr::mk_apply_empty(lex.cur().loc, Nat_name, Nat_type);
 | |
|     assert(Nat_type->produces_nat);
 | |
|     assert(!Nat_type->arity);
 | |
|     assert(field.type->is_nat_subtype);
 | |
|   }
 | |
|   lex.next();
 | |
|   field.register_sym();
 | |
| }
 | |
| 
 | |
| sym::SymDef* register_new_type(const src::SrcLocation& loc, sym_idx_t name) {
 | |
|   // unknown identifier, declare new type
 | |
|   if (!sym::is_uc_ident(name)) {
 | |
|     throw src::ParseError{loc, std::string{"implicitly defined type `"} + sym::symbols.get_name(name) +
 | |
|                                    "` must begin with an uppercase letter"};
 | |
|   }
 | |
|   sym::SymDef* sym_def = sym::define_global_symbol(name, true, loc);
 | |
|   assert(sym_def);
 | |
|   types.emplace_back(types_num++, name);
 | |
|   sym_def->value = new SymValType{&types.back()};
 | |
|   return sym_def;
 | |
| }
 | |
| 
 | |
| void show_tag(std::ostream& os, unsigned long long tag) {
 | |
|   if (!tag) {
 | |
|     return;
 | |
|   }
 | |
|   if (!(tag & ((1ULL << 59) - 1))) {
 | |
|     os << '$';
 | |
|     int c = 0;
 | |
|     while (tag & ((1ULL << 63) - 1)) {
 | |
|       os << (tag >> 63);
 | |
|       tag <<= 1;
 | |
|       ++c;
 | |
|     }
 | |
|     if (!c) {
 | |
|       os << '_';
 | |
|     }
 | |
|   } else {
 | |
|     os << '#';
 | |
|     while (tag & ((1ULL << 63) - 1)) {
 | |
|       static const char hex_digits[] = "0123456789abcdef";
 | |
|       os << hex_digits[tag >> 60];
 | |
|       tag <<= 4;
 | |
|     }
 | |
|     if (!tag) {
 | |
|       os << '_';
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Constructor::show(std::ostream& os, int mode) const {
 | |
|   if (mode & 4) {
 | |
|     os << '[';
 | |
|   } else {
 | |
|     os << sym::symbols.get_name(constr_name);
 | |
|   }
 | |
|   if (!(mode & 8)) {
 | |
|     show_tag(os, tag);
 | |
|   }
 | |
|   for (const Field& field : fields) {
 | |
|     os << ' ';
 | |
|     if (field.implicit || field.constraint) {
 | |
|       if (!(mode & 2)) {
 | |
|         os << '{';
 | |
|       }
 | |
|       if (field.name) {
 | |
|         os << field.get_name() << ':';
 | |
|       }
 | |
|       field.type->show(os, this, 0, mode & ~1);
 | |
|       if (!(mode & 2)) {
 | |
|         os << '}';
 | |
|       }
 | |
|     } else {
 | |
|       if (field.name) {
 | |
|         os << field.get_name() << ':';
 | |
|       }
 | |
|       field.type->show(os, this, 95, mode & ~1);
 | |
|     }
 | |
|   }
 | |
|   if (mode & 4) {
 | |
|     os << " ]";
 | |
|     return;
 | |
|   }
 | |
|   os << " = ";
 | |
|   if (type_defined) {
 | |
|     type_defined->print_name(os);
 | |
|   } else {
 | |
|     os << sym::symbols.get_name(type_name);
 | |
|   }
 | |
|   for (int i = 0; i < type_arity; i++) {
 | |
|     os << ' ';
 | |
|     if (param_negated.at(i)) {
 | |
|       os << '~';
 | |
|     }
 | |
|     params.at(i)->show(os, this, 100, mode | 1);
 | |
|   }
 | |
|   if (!(mode & 2)) {
 | |
|     os << ';';
 | |
|   }
 | |
| }
 | |
| 
 | |
| unsigned long long Constructor::compute_tag() const {
 | |
|   std::ostringstream os;
 | |
|   show(os, 10);
 | |
|   unsigned crc = td::crc32(td::Slice{os.str()});
 | |
|   if (verbosity > 2) {
 | |
|     std::cerr << "crc32('" << os.str() << "') = " << std::hex << crc << std::dec << std::endl;
 | |
|   }
 | |
|   return ((unsigned long long)crc << 32) | 0x80000000;
 | |
| }
 | |
| 
 | |
| bool Constructor::compute_is_fwd() {
 | |
|   if (constr_name || tag_bits || type_arity || fields_num != 1) {
 | |
|     return is_fwd = false;
 | |
|   }
 | |
|   return is_fwd = (!fields[0].implicit && !fields[0].constraint);
 | |
| }
 | |
| 
 | |
| bool show_tag_warnings;
 | |
| 
 | |
| void Constructor::check_assign_tag() {
 | |
|   if (constr_name && (!tag || (tag & (1ULL << 63)) == (1ULL << 63))) {
 | |
|     unsigned long long computed_tag = compute_tag();
 | |
|     if (!tag) {
 | |
|       set_tag(computed_tag);
 | |
|       if (show_tag_warnings) {
 | |
|         std::ostringstream os;
 | |
|         os << "constructor `" << sym::symbols.get_name(type_name) << "::" << sym::symbols.get_name(constr_name)
 | |
|            << "` had no tag, assigned ";
 | |
|         show_tag(os, computed_tag);
 | |
|         where.show_warning(os.str());
 | |
|       }
 | |
|     } else if (tag != computed_tag && show_tag_warnings) {
 | |
|       std::ostringstream os;
 | |
|       os << "constructor `" << sym::symbols.get_name(type_name) << "::" << sym::symbols.get_name(constr_name)
 | |
|          << "` has explicit tag ";
 | |
|       show_tag(os, tag);
 | |
|       os << " different from its computed tag ";
 | |
|       show_tag(os, computed_tag);
 | |
|       where.show_warning(os.str());
 | |
|     }
 | |
|   } else if (!constr_name && !tag) {
 | |
|     set_tag(1ULL << 63);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Type::bind_constructor(const src::SrcLocation& loc, Constructor* cs) {
 | |
|   if (is_final) {
 | |
|     throw src::ParseError{loc, std::string{"cannot add new constructor `"} + sym::symbols.get_name(cs->constr_name) +
 | |
|                                    "` to a finalized type `" + sym::symbols.get_name(type_name) + "`"};
 | |
|   }
 | |
|   if (arity < 0) {
 | |
|     arity = cs->type_arity;
 | |
|     assert(arity >= 0);
 | |
|     args.resize(arity, 0);
 | |
|   } else {
 | |
|     if (arity != cs->type_arity) {
 | |
|       throw src::ParseError{loc, std::string{"parametrized type `"} + sym::symbols.get_name(type_name) +
 | |
|                                      "` redefined with different arity"};
 | |
|     }
 | |
|   }
 | |
|   assert(arity == cs->type_arity && arity == (int)cs->params.size() && cs->params.size() == cs->param_negated.size());
 | |
|   int true_params = 0;
 | |
|   for (int i = 0; i < arity; i++) {
 | |
|     auto expr = cs->params.at(i);
 | |
|     bool negated = cs->param_negated.at(i);
 | |
|     int& x = args[i];
 | |
|     x |= (expr->is_nat ? _IsNat : _IsType);
 | |
|     if ((x & (_IsNat | _IsType)) == (_IsNat | _IsType)) {
 | |
|       throw src::ParseError{expr->where, std::string{"formal parameter to type `"} + sym::symbols.get_name(type_name) +
 | |
|                                              "` has incorrect type"};
 | |
|     }
 | |
|     x |= (negated ? _IsNeg : _IsPos);
 | |
|     if ((x & (_IsPos | _IsNeg)) == (_IsPos | _IsNeg)) {
 | |
|       throw src::ParseError{expr->where, std::string{"formal parameter to type `"} + sym::symbols.get_name(type_name) +
 | |
|                                              "` has incorrect polarity"};
 | |
|     }
 | |
|     if (cs->param_const_val.at(i) < 0) {
 | |
|       x |= _NonConst;
 | |
|     }
 | |
|     true_params += !negated;
 | |
|   }
 | |
|   assert(cs->fields_num >= 0 && (long long)cs->fields_num == (long long)cs->fields.size());
 | |
|   int explicit_fields = 0;
 | |
|   for (Field& field : cs->fields) {
 | |
|     if (field.constraint) {
 | |
|       field.type->bind_value(false, *cs, true);
 | |
|       field.known = true;
 | |
|     } else if (!field.implicit) {
 | |
|       ++explicit_fields;
 | |
|       field.type->bind_value(false, *cs, true);
 | |
|       if (!field.known) {
 | |
|         // field.loc.show_note(std::string{"variable `"} + field.get_name() + "` is assigned a value here");
 | |
|       }
 | |
|       field.known = true;
 | |
|     }
 | |
|   }
 | |
|   cs->is_enum = !explicit_fields;
 | |
|   cs->is_simple_enum = (cs->is_enum && !true_params);
 | |
|   for (int i = 0; i < arity; i++) {
 | |
|     auto expr = cs->params[i];
 | |
|     bool negated = cs->param_negated[i];
 | |
|     if (negated) {
 | |
|       expr->bind_value(true, *cs);
 | |
|     }
 | |
|   }
 | |
|   for (Field& field : cs->fields) {
 | |
|     if (!field.known) {
 | |
|       throw src::ParseError{field.loc, std::string{"field `"} + field.get_name() + "` is left unbound"};
 | |
|     }
 | |
|   }
 | |
|   if (cs->constr_name) {
 | |
|     for (auto c : constructors) {
 | |
|       if (c->constr_name == cs->constr_name) {
 | |
|         std::string tname = sym::symbols.get_name(type_name);
 | |
|         std::string cname = tname + "::" + sym::symbols.get_name(cs->constr_name);
 | |
|         c->where.show_note(std::string{"constructor `"} + cname + "` first defined here");
 | |
|         throw src::ParseError{cs->where, std::string{"constructor `"} + cname + "` redefined"};
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (!cs->type_defined && cs->type_name == type_name) {
 | |
|     cs->type_defined = this;
 | |
|   }
 | |
|   cs->check_assign_tag();
 | |
|   cs->compute_is_fwd();
 | |
|   is_enum &= cs->is_enum;
 | |
|   is_simple_enum &= cs->is_simple_enum;
 | |
|   if (constr_num && (is_special != cs->is_special)) {
 | |
|     throw src::ParseError{cs->where, std::string{"type `"} + sym::symbols.get_name(type_name) +
 | |
|                                          "` has mixed special and non-special constructors"};
 | |
|   }
 | |
|   is_special = cs->is_special;
 | |
|   ++(constr_num);
 | |
|   constructors.push_back(cs);
 | |
| }
 | |
| 
 | |
| bool Type::unique_constructor_equals(const Constructor& cs, bool allow_other_names) const {
 | |
|   return constr_num == 1 && constructors.at(0)->isomorphic_to(cs, allow_other_names);
 | |
| }
 | |
| 
 | |
| bool Constructor::isomorphic_to(const Constructor& cs, bool allow_other_names) const {
 | |
|   if (constr_name != cs.constr_name || tag != cs.tag || fields_num != cs.fields_num || type_arity != cs.type_arity ||
 | |
|       params.size() != cs.params.size()) {
 | |
|     return false;
 | |
|   }
 | |
|   for (int i = 0; i < fields_num; i++) {
 | |
|     if (!fields.at(i).isomorphic_to(cs.fields.at(i), allow_other_names)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   for (std::size_t i = 0; i < params.size(); i++) {
 | |
|     if (!params.at(i)->equal(*cs.params.at(i))) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool Field::isomorphic_to(const Field& f, bool allow_other_names) const {
 | |
|   if (f.field_idx != field_idx || f.implicit != implicit || f.constraint != constraint ||
 | |
|       (!allow_other_names && f.name != name)) {
 | |
|     return false;
 | |
|   }
 | |
|   return f.type->equal(*type);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *  TL-B SOURCE PARSER
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| void parse_field_list(Lexer& lex, Constructor& cs);
 | |
| 
 | |
| TypeExpr* parse_anonymous_constructor(Lexer& lex, Constructor& cs) {
 | |
|   sym::open_scope(lex);
 | |
|   Constructor* cs2 = new (AR) Constructor(lex.cur().loc);  // anonymous constructor
 | |
|   parse_field_list(lex, *cs2);
 | |
|   if (lex.tp() != ']') {
 | |
|     lex.expect(']');
 | |
|   }
 | |
|   cs2->set_tag(1ULL << 63);
 | |
|   for (int i = builtin_types_num; i < types_num; i++) {
 | |
|     if (types.at(i).is_auto && types[i].is_final && types[i].unique_constructor_equals(*cs2)) {
 | |
|       sym::close_scope(lex);
 | |
|       if (types[i].parent_type_idx >= 0) {
 | |
|         types[i].parent_type_idx = -2;
 | |
|       }
 | |
|       delete cs2;
 | |
|       return TypeExpr::mk_apply_empty(lex.cur().loc, 0, &types[i]);
 | |
|     }
 | |
|   }
 | |
|   types.emplace_back(types_num++, 0);
 | |
|   Type* type = &types.back();  // anonymous type
 | |
|   type->bind_constructor(lex.cur().loc, cs2);
 | |
|   type->is_final = true;
 | |
|   type->is_auto = true;
 | |
|   type->is_anon = true;
 | |
|   type->renew_last_declared();
 | |
|   sym::close_scope(lex);
 | |
|   return TypeExpr::mk_apply_empty(lex.cur().loc, 0, type);
 | |
| }
 | |
| 
 | |
| TypeExpr* parse_expr(Lexer& lex, Constructor& cs, int mode);
 | |
| 
 | |
| // ( E ) | [ {field-def} ] | id | ~id | num | ^T
 | |
| TypeExpr* parse_term(Lexer& lex, Constructor& cs, int mode) {
 | |
|   if (lex.tp() == '(') {
 | |
|     lex.next();
 | |
|     TypeExpr* expr = parse_expr(lex, cs, mode);
 | |
|     expr->check_mode(lex.cur().loc, mode);
 | |
|     lex.expect(')');
 | |
|     return expr;
 | |
|   }
 | |
|   if (lex.tp() == src::_Number) {
 | |
|     TypeExpr* expr = TypeExpr::mk_intconst(lex.cur().loc, lex.cur().str);
 | |
|     expr->check_mode(lex.cur().loc, mode);
 | |
|     lex.next();
 | |
|     return expr;
 | |
|   }
 | |
|   if (lex.tp() == '[') {
 | |
|     lex.next();
 | |
|     TypeExpr* expr = parse_anonymous_constructor(lex, cs);
 | |
|     expr->check_mode(lex.cur().loc, mode);
 | |
|     lex.expect(']');
 | |
|     return expr;
 | |
|   }
 | |
|   if (lex.tp() == '^') {
 | |
|     src::SrcLocation loc = lex.cur().loc;
 | |
|     lex.next();
 | |
|     TypeExpr* expr = parse_term(lex, cs, mode & ~2);
 | |
|     expr->close(lex.cur().loc);
 | |
|     if (expr->is_nat) {
 | |
|       throw src::ParseError{loc, "cannot create a cell reference type to a natural number"};
 | |
|     }
 | |
|     return TypeExpr::mk_cellref(loc, expr);
 | |
|   }
 | |
|   bool negate = false;
 | |
|   if (lex.tp() == '~') {
 | |
|     lex.next();
 | |
|     if (lex.tp() != src::_Ident) {
 | |
|       lex.expect(src::_Ident, "field identifier");
 | |
|     }
 | |
|     negate = true;
 | |
|   }
 | |
|   if (lex.tp() == src::_Ident) {
 | |
|     sym_idx_t name = lex.cur().val;
 | |
|     sym::SymDef* sym_def = sym::lookup_symbol(name);
 | |
|     if (!sym_def) {
 | |
|       if (negate) {
 | |
|         throw src::ParseError{lex.cur().loc, "field identifier expected"};
 | |
|       }
 | |
|       sym_def = register_new_type(lex.cur().loc, name);
 | |
|       if (verbosity > 2) {
 | |
|         std::cerr << "implicitly defined new type `" << sym::symbols.get_name(name) << "`" << std::endl;
 | |
|       }
 | |
|     }
 | |
|     if (!sym_def->value) {
 | |
|       throw src::ParseError{lex.cur().loc, "global symbol has no value"};
 | |
|     }
 | |
|     if (sym_def->value->type == sym::SymValBase::_Typename) {
 | |
|       // found a global type identifier
 | |
|       if (negate) {
 | |
|         throw src::ParseError{lex.cur().loc, "cannot negate a type"};
 | |
|       }
 | |
|       SymValType* svt = dynamic_cast<SymValType*>(sym_def->value);
 | |
|       assert(svt && svt->type_ref);
 | |
|       (svt->type_ref->used)++;
 | |
|       auto res = TypeExpr::mk_apply_empty(lex.cur().loc, name, svt->type_ref);
 | |
|       lex.next();
 | |
|       return res;
 | |
|     }
 | |
|     SymVal* sym_val = dynamic_cast<SymVal*>(sym_def->value);
 | |
|     if (sym_def->value->type != sym::SymValBase::_Param || !sym_val) {
 | |
|       throw src::ParseError{lex.cur().loc, "field identifier expected"};
 | |
|     }
 | |
|     if (sym_def->level != sym::scope_level) {
 | |
|       throw src::ParseError{lex.cur().loc, std::string{"cannot access field `"} + lex.cur().str + "` from outer scope"};
 | |
|     }
 | |
|     int i = sym_val->idx;
 | |
|     assert(i >= 0 && i < cs.fields_num);
 | |
|     auto res = new (AR) TypeExpr{lex.cur().loc, TypeExpr::te_Param, i};
 | |
|     auto field_type = cs.fields[i].type;
 | |
|     assert(field_type);
 | |
|     if ((mode & 4) && !cs.fields[i].known) {
 | |
|       // auto-negate, used for parsing type parameters in constructor RHS
 | |
|       negate = true;
 | |
|     }
 | |
|     res->is_nat = field_type->is_nat_subtype;
 | |
|     // std::cerr << "using field " << lex.cur().str << "; is_nat_subtype = " << res->is_nat << std::endl;
 | |
|     if (!res->is_nat && field_type->tp != TypeExpr::te_Type) {
 | |
|       throw src::ParseError{lex.cur().loc,
 | |
|                             "cannot use a field in an expression unless it is either an integer or a type"};
 | |
|     }
 | |
|     if (negate && !cs.fields[i].implicit) {
 | |
|       throw src::ParseError{lex.cur().loc, "cannot negate an explicit field"};
 | |
|     }
 | |
|     res->negated = negate;
 | |
|     res->check_mode(lex.cur().loc, mode);
 | |
|     lex.next();
 | |
|     return res;
 | |
|   } else {
 | |
|     lex.expect(src::_Ident, "type identifier");
 | |
|     return nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // E[.E]
 | |
| TypeExpr* parse_expr97(Lexer& lex, Constructor& cs, int mode) {
 | |
|   TypeExpr* expr = parse_term(lex, cs, mode | 3);
 | |
|   if (lex.tp() == '.') {
 | |
|     src::SrcLocation where = lex.cur().loc;
 | |
|     expr->close(lex.cur().loc);
 | |
|     // std::cerr << "parse ., mode " << mode << std::endl;
 | |
|     if (!(mode & 2)) {
 | |
|       throw src::ParseError{where, "bitfield expression cannot be used instead of a type expression"};
 | |
|     }
 | |
|     if (!expr->is_nat) {
 | |
|       throw src::ParseError{where, "cannot apply bit selection operator `.` to types"};
 | |
|     }
 | |
|     lex.next();
 | |
|     TypeExpr* expr2 = parse_term(lex, cs, mode & ~1);
 | |
|     expr2->close(lex.cur().loc);
 | |
|     if (expr->negated || expr2->negated) {
 | |
|       throw src::ParseError{where, "cannot apply bit selection operator `.` to values of negative polarity"};
 | |
|     }
 | |
|     expr = TypeExpr::mk_apply(where, TypeExpr::te_GetBit, expr, expr2);
 | |
|   }
 | |
|   expr->check_mode(lex.cur().loc, mode);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| // E ? E [ : E ]
 | |
| TypeExpr* parse_expr95(Lexer& lex, Constructor& cs, int mode) {
 | |
|   TypeExpr* expr = parse_expr97(lex, cs, mode | 3);
 | |
|   if (lex.tp() != '?') {
 | |
|     expr->check_mode(lex.cur().loc, mode);
 | |
|     return expr;
 | |
|   }
 | |
|   src::SrcLocation where = lex.cur().loc;
 | |
|   expr->close(where);
 | |
|   if (!expr->is_nat) {
 | |
|     throw src::ParseError{where, "cannot apply `?` with non-integer selectors"};
 | |
|   }
 | |
|   lex.next();
 | |
|   TypeExpr* expr2 = parse_term(lex, cs, mode & ~10);
 | |
|   expr2->close(lex.cur().loc);
 | |
|   expr2->no_tchk();
 | |
|   expr = TypeExpr::mk_apply(where, TypeExpr::te_CondType, expr, expr2);
 | |
|   expr->check_mode(lex.cur().loc, mode);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| // E E
 | |
| TypeExpr* parse_expr90(Lexer& lex, Constructor& cs, int mode) {
 | |
|   TypeExpr* expr = parse_expr95(lex, cs, mode | 3);
 | |
|   while (lex.tp() == '(' || lex.tp() == src::_Ident || lex.tp() == src::_Number || lex.tp() == '~' || lex.tp() == '^' ||
 | |
|          lex.tp() == '[') {
 | |
|     TypeExpr* expr2 = parse_expr95(lex, cs, mode | 3);
 | |
|     expr2->close(lex.cur().loc);
 | |
|     expr = TypeExpr::mk_apply_gen(lex.cur().loc, expr, expr2);
 | |
|   }
 | |
|   expr->check_mode(lex.cur().loc, mode);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| // E * E
 | |
| TypeExpr* parse_expr30(Lexer& lex, Constructor& cs, int mode) {
 | |
|   TypeExpr* expr = parse_expr90(lex, cs, mode);
 | |
|   while (lex.tp() == '*') {
 | |
|     src::SrcLocation where = lex.cur().loc;
 | |
|     expr->close(lex.cur().loc);
 | |
|     if (!expr->is_nat) {
 | |
|       throw src::ParseError{where, "cannot apply `*` to types"};
 | |
|     }
 | |
|     lex.next();
 | |
|     TypeExpr* expr2 = parse_expr90(lex, cs, mode);
 | |
|     expr2->close(lex.cur().loc);
 | |
|     if (expr2->is_nat) {
 | |
|       expr = TypeExpr::mk_mulint(where, expr, expr2);
 | |
|     } else {
 | |
|       expr2->no_tchk();
 | |
|       expr = TypeExpr::mk_apply(where, TypeExpr::te_Tuple, expr, expr2);
 | |
|     }
 | |
|   }
 | |
|   expr->check_mode(lex.cur().loc, mode);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| // E + E
 | |
| TypeExpr* parse_expr20(Lexer& lex, Constructor& cs, int mode) {
 | |
|   TypeExpr* expr = parse_expr30(lex, cs, mode);
 | |
|   while (lex.tp() == '+') {
 | |
|     src::SrcLocation where = lex.cur().loc;
 | |
|     expr->close(lex.cur().loc);
 | |
|     // std::cerr << "parse +, mode " << mode << std::endl;
 | |
|     if (!(mode & 2)) {
 | |
|       throw src::ParseError{where, "sum cannot be used instead of a type expression"};
 | |
|     }
 | |
|     if (!expr->is_nat) {
 | |
|       throw src::ParseError{where, "cannot apply `+` to types"};
 | |
|     }
 | |
|     lex.next();
 | |
|     TypeExpr* expr2 = parse_expr30(lex, cs, mode & ~1);
 | |
|     expr2->close(lex.cur().loc);
 | |
|     if (expr->negated && expr2->negated) {
 | |
|       throw src::ParseError{where, "cannot add two values of negative polarity"};
 | |
|     }
 | |
|     bool negated = expr->negated | expr2->negated;
 | |
|     expr = TypeExpr::mk_apply(where, TypeExpr::te_Add, expr, expr2);
 | |
|     expr->negated = negated;
 | |
|   }
 | |
|   expr->check_mode(lex.cur().loc, mode);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| // E | E = E | E <= E | E < E | E >= E | E > E
 | |
| TypeExpr* parse_expr10(Lexer& lex, Constructor& cs, int mode) {
 | |
|   TypeExpr* expr = parse_expr20(lex, cs, mode | 3);
 | |
|   int op = lex.tp();
 | |
|   if (!(op == '=' || op == '<' || op == '>' || op == src::_Leq || op == src::_Geq)) {
 | |
|     expr->check_mode(lex.cur().loc, mode);
 | |
|     return expr;
 | |
|   }
 | |
|   // std::cerr << "parse <=>, mode " << mode << std::endl;
 | |
|   sym_idx_t op_name = lex.cur().val;
 | |
|   src::SrcLocation where = lex.cur().loc;
 | |
|   expr->close(where);
 | |
|   if (!(mode & 1)) {
 | |
|     throw src::ParseError{where, "comparison result used as an integer"};
 | |
|   }
 | |
|   if (!expr->is_nat) {
 | |
|     throw src::ParseError{where, "cannot apply integer comparison to types"};
 | |
|   }
 | |
|   lex.next();
 | |
|   TypeExpr* expr2 = parse_expr20(lex, cs, (mode & ~1) | 2);
 | |
|   expr2->close(lex.cur().loc);
 | |
|   if (!expr2->is_nat) {
 | |
|     throw src::ParseError{lex.cur().loc, "cannot apply integer comparison to types"};
 | |
|   }
 | |
|   if (op == '>') {
 | |
|     std::swap(expr, expr2);
 | |
|     op = '<';
 | |
|     op_name = Less_name;
 | |
|   } else if (op == src::_Geq) {
 | |
|     std::swap(expr, expr2);
 | |
|     op = src::_Leq;
 | |
|     op_name = Leq_name;
 | |
|   }
 | |
|   auto sym_def = sym::lookup_symbol(op_name, 2);
 | |
|   assert(sym_def);
 | |
|   auto sym_val = dynamic_cast<SymValType*>(sym_def->value);
 | |
|   assert(sym_val);
 | |
|   auto expr0 = TypeExpr::mk_apply_empty(where, op_name, sym_val->type_ref);
 | |
|   expr = TypeExpr::mk_apply_gen(where, std::move(expr0), expr);
 | |
|   expr = TypeExpr::mk_apply_gen(lex.cur().loc, expr, std::move(expr2));
 | |
|   expr->check_mode(lex.cur().loc, mode);
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| TypeExpr* parse_expr(Lexer& lex, Constructor& cs, int mode) {
 | |
|   return parse_expr10(lex, cs, mode);
 | |
| }
 | |
| 
 | |
| void parse_param(Lexer& lex, Constructor& cs, bool named) {
 | |
|   // [ ( ident | _ ) : ] type-expr
 | |
|   src::SrcLocation loc = lex.cur().loc;
 | |
|   if (named && lex.tp() == '_') {
 | |
|     lex.next();
 | |
|     lex.expect(':');
 | |
|     named = false;
 | |
|   }
 | |
|   sym_idx_t param_name = 0;
 | |
|   if (named) {
 | |
|     if (lex.tp() != src::_Ident) {
 | |
|       lex.expect(src::_Ident);
 | |
|     }
 | |
|     param_name = lex.cur().val;
 | |
|     lex.next();
 | |
|     lex.expect(':');
 | |
|   }
 | |
|   Field& field = cs.new_field(loc, false, param_name);
 | |
|   field.type = parse_expr95(lex, cs, 9);  // must be a type expression
 | |
|   field.type->close(lex.cur().loc);
 | |
|   field.type->detect_constexpr();
 | |
|   field.subrec = field.type->is_ref_to_anon();
 | |
|   CHECK(!field.name || !field.subrec);
 | |
|   field.register_sym();
 | |
| }
 | |
| 
 | |
| void parse_constraint(Lexer& lex, Constructor& cs) {
 | |
|   Field& field = cs.new_field(lex.cur().loc, true, 0);
 | |
|   field.type = parse_expr(lex, cs, 9);  // must be a type expression
 | |
|   field.type->close(lex.cur().loc);
 | |
|   field.type->detect_constexpr();
 | |
|   field.constraint = true;
 | |
|   field.register_sym();
 | |
| }
 | |
| 
 | |
| void parse_field_list(Lexer& lex, Constructor& cs) {
 | |
|   while (lex.tp() != '=' && lex.tp() != ']') {
 | |
|     if (lex.tp() == '{') {
 | |
|       // either an implicit parameter or a constraint
 | |
|       lex.next();
 | |
|       if (lex.tp() == src::_Ident && lex.peek().tp == ':') {
 | |
|         parse_implicit_param(lex, cs);
 | |
|       } else {
 | |
|         parse_constraint(lex, cs);
 | |
|       }
 | |
|       lex.expect('}');
 | |
|     } else if ((lex.tp() == src::_Ident || lex.tp() == '_') && lex.peek().tp == ':') {
 | |
|       parse_param(lex, cs, true);
 | |
|     } else {
 | |
|       parse_param(lex, cs, false);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void parse_constructor_def(Lexer& lex) {
 | |
|   if (lex.tp() != '_' && (lex.tp() != src::_Ident || !sym::is_lc_ident(lex.cur().val))) {
 | |
|     throw src::ParseError{lex.cur().loc, "constructor name lowercase identifier expected"};
 | |
|   }
 | |
|   bool is_special = sym::is_spec_lc_ident(lex.cur().val);
 | |
|   sym::open_scope(lex);
 | |
|   Lexem constr_lex = lex.cur();
 | |
|   int orig_types_num = types_num;
 | |
|   sym_idx_t constr_name = (lex.tp() == src::_Ident ? lex.cur().val : 0);
 | |
|   src::SrcLocation where = lex.cur().loc;
 | |
|   lex.next();
 | |
|   unsigned long long tag = 0;
 | |
|   if (lex.tp() == src::_Special) {
 | |
|     tag = src::get_special_value(lex.cur().str);
 | |
|     assert(tag);
 | |
|     lex.next();
 | |
|   }
 | |
|   //std::cerr << "parsing constructor `" << sym::symbols.get_name(constr_name) << "` with tag " << std::hex << tag
 | |
|   //          << std::dec << std::endl;
 | |
|   auto cs_ref = new (AR) Constructor(where, constr_name, 0, tag);
 | |
|   Constructor& cs = *cs_ref;
 | |
|   cs.is_special = is_special;
 | |
|   parse_field_list(lex, cs);
 | |
|   lex.expect('=');
 | |
|   if (lex.tp() != src::_Ident || !sym::is_uc_ident(lex.cur().val)) {
 | |
|     throw src::ParseError{lex.cur().loc, "type name uppercase identifier expected"};
 | |
|   }
 | |
|   Lexem type_lex = lex.cur();
 | |
|   sym_idx_t type_name = lex.cur().val;
 | |
|   sym::SymDef* sym_def = sym::lookup_symbol(type_name, 2);
 | |
|   if (!sym_def) {
 | |
|     sym_def = register_new_type(lex.cur().loc, type_name);
 | |
|     if (verbosity > 2) {
 | |
|       std::cerr << "defined new type `" << sym::symbols.get_name(type_name) << "`" << std::endl;
 | |
|     }
 | |
|     assert(sym_def);
 | |
|   }
 | |
|   if (!sym_def || !sym_def->value || sym_def->value->type != sym::SymValBase::_Typename) {
 | |
|     throw src::ParseError{lex.cur().loc, "parametrized type identifier expected"};
 | |
|   }
 | |
|   SymValType* sym_val = dynamic_cast<SymValType*>(sym_def->value);
 | |
|   assert(sym_val);
 | |
|   cs.type_name = type_name;
 | |
|   cs.type_arity = 0;
 | |
|   Type* type = cs.type_defined = sym_val->type_ref;
 | |
|   if (type->is_final) {
 | |
|     throw src::ParseError{lex.cur().loc,
 | |
|                           std::string{"cannot add new constructor to a finalized type `"} + lex.cur().str + "`"};
 | |
|   }
 | |
|   lex.next();
 | |
|   while (lex.tp() != ';') {
 | |
|     bool negate = (lex.tp() == '~');
 | |
|     if (negate) {
 | |
|       lex.next();
 | |
|     }
 | |
|     TypeExpr* type_param = parse_term(lex, cs, negate ? 3 : 7);
 | |
|     type_param->close(lex.cur().loc);
 | |
|     int const_val = (!negate && type_param->tp == TypeExpr::te_IntConst) ? type_param->value : -1;
 | |
|     if (!negate) {
 | |
|       //std::cerr << "binding value to type parameter expression ";
 | |
|       //type_param->show(std::cerr, &cs);
 | |
|       //std::cerr << std::endl;
 | |
|       type_param->bind_value(negate, cs);
 | |
|     } else if (!type_param->is_nat) {
 | |
|       throw src::ParseError{type_param->where, "cannot return type expressions"};
 | |
|     }
 | |
|     cs.params.push_back(type_param);
 | |
|     cs.param_negated.push_back(negate);
 | |
|     cs.param_const_val.push_back(const_val);
 | |
|     ++cs.type_arity;
 | |
|   }
 | |
|   if (lex.tp() != ';') {
 | |
|     lex.expect(';');
 | |
|   }
 | |
|   type->bind_constructor(lex.cur().loc, cs_ref);
 | |
|   type->renew_last_declared();
 | |
|   lex.expect(';');
 | |
|   sym::close_scope(lex);
 | |
|   for (int i = orig_types_num; i < types_num; i++) {
 | |
|     if (types.at(i).is_auto && types[i].parent_type_idx == -1) {
 | |
|       types[i].parent_type_idx = type->type_idx;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *  SOURCE PARSER (TOP LEVEL)
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| std::vector<const src::FileDescr*> source_fdescr;
 | |
| 
 | |
| bool parse_source(std::istream* is, src::FileDescr* fdescr) {
 | |
|   src::SourceReader reader{is, fdescr};
 | |
|   src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/"};
 | |
|   while (lex.tp() != src::_Eof) {
 | |
|     parse_constructor_def(lex);
 | |
|     // std::cerr << lex.cur().str << '\t' << lex.cur().name_str() << std::endl;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool parse_source_file(const char* filename) {
 | |
|   if (!filename || !*filename) {
 | |
|     throw src::Fatal{"source file name is an empty string"};
 | |
|   }
 | |
|   src::FileDescr* cur_source = new src::FileDescr{filename};
 | |
|   source_fdescr.push_back(cur_source);
 | |
|   std::ifstream ifs{filename};
 | |
|   if (ifs.fail()) {
 | |
|     throw src::Fatal{std::string{"cannot open source file `"} + filename + "`"};
 | |
|   }
 | |
|   return parse_source(&ifs, cur_source);
 | |
| }
 | |
| 
 | |
| bool parse_source_stdin() {
 | |
|   src::FileDescr* cur_source = new src::FileDescr{"stdin", true};
 | |
|   source_fdescr.push_back(cur_source);
 | |
|   return parse_source(&std::cin, cur_source);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *   BUILT-IN TYPE DEFINITIONS
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| Type* define_builtin_type(std::string name_str, std::string args, bool produces_nat, int size = -1, int min_size = -1,
 | |
|                           bool any_bits = false, int is_int = 0) {
 | |
|   sym_idx_t name = sym::symbols.lookup_add(name_str);
 | |
|   assert(name_str.size() && name);
 | |
|   int arity = (int)args.size();
 | |
|   types.emplace_back(types_num++, name, produces_nat, arity, true, true);
 | |
|   auto type = &types.back();
 | |
|   type->args.resize(arity, 0);
 | |
|   int f = (name_str != "#" ? Type::_IsPos : 0);
 | |
|   for (int i = 0; i < arity; i++) {
 | |
|     type->args[i] = f | (args[i] == '#' ? Type::_IsNat : Type::_IsType);
 | |
|   }
 | |
|   if (is_int) {
 | |
|     type->is_integer = (char)is_int;
 | |
|   }
 | |
|   auto sym_def = sym::define_global_symbol(name, true);
 | |
|   assert(sym_def);
 | |
|   sym_def->value = new (AR) SymValType{type};
 | |
|   if (size < 0) {
 | |
|     type->size = MinMaxSize::Any;
 | |
|   } else if (min_size >= 0 && min_size != size) {
 | |
|     type->size = MinMaxSize::size_range(min_size, size);
 | |
|   } else {
 | |
|     type->size = MinMaxSize::fixed_size(size);
 | |
|     type->has_fixed_size = true;
 | |
|   }
 | |
|   type->any_bits = any_bits;
 | |
|   return type;
 | |
| }
 | |
| 
 | |
| Type* lookup_type(std::string name_str) {
 | |
|   sym_idx_t name = sym::symbols.lookup(name_str);
 | |
|   if (name) {
 | |
|     auto sym_def = sym::lookup_symbol(name);
 | |
|     if (sym_def) {
 | |
|       auto sym_val = dynamic_cast<SymValType*>(sym_def->value);
 | |
|       if (sym_val) {
 | |
|         return sym_val->type_ref;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| void define_builtins() {
 | |
|   types.reserve(10000);
 | |
|   Nat_type = define_builtin_type("#", "", true, 32, 32, true);
 | |
|   NatWidth_type = define_builtin_type("##", "#", true, 32, 0, true);
 | |
|   NatLess_type = define_builtin_type("#<", "#", true, 32, 0);
 | |
|   NatLeq_type = define_builtin_type("#<=", "#", true, 32, 0);
 | |
|   Any_type = define_builtin_type("Any", "", false);
 | |
|   Cell_type = define_builtin_type("Cell", "", false);
 | |
|   Int_type = define_builtin_type("int", "#", false, 257, 0, true, -1);
 | |
|   UInt_type = define_builtin_type("uint", "#", false, 256, 0, true, 1);
 | |
|   Bits_type = define_builtin_type("bits", "#", false, 1023, 0, true, 0);
 | |
|   for (int i = 1; i <= 257; i++) {
 | |
|     char buff[8];
 | |
|     sprintf(buff, "uint%d", i);
 | |
|     define_builtin_type(buff + 1, "", false, i, i, true, -1);
 | |
|     if (i < 257) {
 | |
|       define_builtin_type(buff, "", false, i, i, true, 1);
 | |
|     }
 | |
|   }
 | |
|   for (int i = 1; i <= 1023; i++) {
 | |
|     char buff[12];
 | |
|     sprintf(buff, "bits%d", i);
 | |
|     define_builtin_type(buff, "", false, i, i, true, 0);
 | |
|   }
 | |
|   Eq_type = define_builtin_type("=", "##", false, 0, 0, true);
 | |
|   Less_type = define_builtin_type("<", "##", false, 0, 0, true);
 | |
|   Leq_type = define_builtin_type("<=", "##", false, 0, 0, true);
 | |
|   Nat_name = sym::symbols.lookup("#");
 | |
|   Eq_name = sym::symbols.lookup("=");
 | |
|   Less_name = sym::symbols.lookup("<");
 | |
|   Leq_name = sym::symbols.lookup("<=");
 | |
|   builtin_types_num = types_num;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *  SCHEME PROCESSING AND CHECKING
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| bool Type::cons_all_exact() const {
 | |
|   unsigned long long sum = 0;
 | |
|   for (const auto& cons : constructors) {
 | |
|     sum += (1ULL << (63 - cons->tag_bits));
 | |
|   }
 | |
|   return sum == (1ULL << 63);
 | |
| }
 | |
| 
 | |
| int Type::cons_common_len() const {
 | |
|   if (!constr_num) {
 | |
|     return -1;
 | |
|   }
 | |
|   int len = constructors.at(0)->tag_bits;
 | |
|   for (const auto cons : constructors) {
 | |
|     if (cons->tag_bits != len) {
 | |
|       return -1;
 | |
|     }
 | |
|   }
 | |
|   return len;
 | |
| }
 | |
| 
 | |
| bool Constructor::compute_admissible_params() {
 | |
|   int dim = 0;
 | |
|   int abs_param[4];
 | |
|   for (std::size_t i = 0; i < params.size(); i++) {
 | |
|     if (!param_negated[i] && params[i]->is_nat) {
 | |
|       int t = params[i]->abstract_interpret_nat();
 | |
|       assert(t >= 0 && t <= 15);
 | |
|       // std::cerr << "abstract_interpret( " << params[i] << " ) = " << t << std::endl;
 | |
|       abs_param[dim++] = t;
 | |
|       if (!t) {
 | |
|         admissible_params.clear_all();
 | |
|         return false;
 | |
|       }
 | |
|       if (dim == 4) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   while (dim > 0 && abs_param[dim - 1] == 15) {
 | |
|     --dim;
 | |
|   }
 | |
|   if (!dim) {
 | |
|     admissible_params.set_all();
 | |
|     return true;
 | |
|   }
 | |
|   admissible_params.set_by_pattern(dim, abs_param);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool Type::compute_admissible_params() {
 | |
|   bool admissible = false;
 | |
|   for (Constructor* cs : constructors) {
 | |
|     admissible |= cs->compute_admissible_params();
 | |
|     admissible_params |= cs->admissible_params;
 | |
|   }
 | |
|   return admissible;
 | |
| }
 | |
| 
 | |
| void compute_admissible_params() {
 | |
|   for (int i = builtin_types_num; i < types_num; i++) {
 | |
|     (void)types[i].compute_admissible_params();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool Constructor::recompute_begins_with() {
 | |
|   for (const Field& field : fields) {
 | |
|     if (!field.implicit && !field.constraint) {
 | |
|       TypeExpr* expr = field.type;
 | |
|       if (expr->tp == TypeExpr::te_Ref) {
 | |
|         continue;
 | |
|       }
 | |
|       if (expr->tp != TypeExpr::te_Apply) {
 | |
|         break;
 | |
|       }
 | |
|       BitPfxCollection add = expr->type_applied->begins_with * tag;
 | |
|       return (begins_with += add);
 | |
|     }
 | |
|   }
 | |
|   BitPfxCollection add{tag};
 | |
|   if (begins_with == add) {
 | |
|     return false;
 | |
|   }
 | |
|   begins_with += add;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool Type::recompute_begins_with() {
 | |
|   bool changes = false;
 | |
|   for (Constructor* cs : constructors) {
 | |
|     if (cs->recompute_begins_with()) {
 | |
|       changes |= (begins_with += cs->begins_with);
 | |
|     }
 | |
|   }
 | |
|   return changes;
 | |
| }
 | |
| 
 | |
| void compute_begins_with() {
 | |
|   bool changes = true;
 | |
|   while (changes) {
 | |
|     changes = false;
 | |
|     for (int i = builtin_types_num; i < types_num; i++) {
 | |
|       changes |= types[i].recompute_begins_with();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool Constructor::recompute_minmax_size() {
 | |
|   MinMaxSize sz = MinMaxSize::fixed_size(tag_bits);
 | |
|   for (const Field& field : fields) {
 | |
|     if (!field.implicit && !field.constraint) {
 | |
|       sz += field.type->compute_size();
 | |
|     }
 | |
|   }
 | |
|   if (sz == size) {
 | |
|     return false;
 | |
|   }
 | |
|   size = sz;
 | |
|   has_fixed_size = sz.is_fixed();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool Type::recompute_minmax_size() {
 | |
|   MinMaxSize sz;
 | |
|   bool changes = false;
 | |
|   for (Constructor* cs : constructors) {
 | |
|     changes |= cs->recompute_minmax_size();
 | |
|     sz |= cs->size;
 | |
|   }
 | |
|   if (sz == size) {
 | |
|     return changes;
 | |
|   }
 | |
|   size = sz;
 | |
|   has_fixed_size = sz.is_fixed();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void compute_minmax_sizes() {
 | |
|   bool changes = true;
 | |
|   while (changes) {
 | |
|     changes = false;
 | |
|     for (int i = builtin_types_num; i < types_num; i++) {
 | |
|       changes |= types[i].recompute_minmax_size();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool Constructor::recompute_any_bits() {
 | |
|   bool res = true;
 | |
|   for (const Field& field : fields) {
 | |
|     if (!field.implicit && !field.constraint) {
 | |
|       res &= field.type->compute_any_bits();
 | |
|     }
 | |
|   }
 | |
|   if (res == any_bits) {
 | |
|     return false;
 | |
|   }
 | |
|   any_bits = res;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool Type::recompute_any_bits() {
 | |
|   bool res = begins_with.is_all();
 | |
|   bool changes = false;
 | |
|   for (Constructor* cs : constructors) {
 | |
|     changes |= cs->recompute_any_bits();
 | |
|     res &= cs->any_bits;
 | |
|   }
 | |
|   if (res == any_bits) {
 | |
|     return changes;
 | |
|   }
 | |
|   any_bits = res;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void compute_any_bits() {
 | |
|   bool changes = true;
 | |
|   while (changes) {
 | |
|     changes = false;
 | |
|     for (int i = builtin_types_num; i < types_num; i++) {
 | |
|       changes |= types[i].recompute_any_bits();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Type::detect_basic_types() {
 | |
|   if (!arity && constr_num > 0 && size.is_fixed() && any_bits) {
 | |
|     is_unit = !size.min_size();
 | |
|     is_bool = (size.min_size() == 0x100);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void detect_basic_types() {
 | |
|   for (int i = builtin_types_num; i < types_num; i++) {
 | |
|     types[i].detect_basic_types();
 | |
|   }
 | |
| }
 | |
| 
 | |
| int show_size_warnings() {
 | |
|   int errors = 0;
 | |
|   for (int i = builtin_types_num; i < types_num; i++) {
 | |
|     Type& type = types[i];
 | |
|     if (!type.size.fits_into_cell() || !type.size.is_possible()) {
 | |
|       std::cerr << "error: type `" << type.get_name() << "`"
 | |
|                 << (!type.size.is_possible() ? " cannot be instantiated" : " never fits into a cell") << " (size "
 | |
|                 << type.size << ")\n";
 | |
|       ++errors;
 | |
|     }
 | |
|     for (Constructor* cs : type.constructors) {
 | |
|       if (!cs->size.fits_into_cell() || !cs->size.is_possible()) {
 | |
|         std::cerr << "error: constructor `" << cs->get_qualified_name() << "`"
 | |
|                   << (!cs->size.is_possible() ? " cannot be instantiated" : " never fits into a cell") << " (size "
 | |
|                   << cs->size << ")\n";
 | |
|         cs->show(std::cerr);
 | |
|         std::cerr << std::endl;
 | |
|         cs->where.show_note("defined here");
 | |
|         ++errors;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return errors;
 | |
| }
 | |
| 
 | |
| bool Type::is_const_arg(int p) const {
 | |
|   return (args.at(p) & (_IsType | _IsNat | _IsPos | _IsNeg | _NonConst)) == (_IsNat | _IsPos);
 | |
| }
 | |
| 
 | |
| int Type::detect_const_params() {
 | |
|   for (int i = 0; i < arity; i++) {
 | |
|     if (is_const_arg(i)) {
 | |
|       return const_param_idx = i;
 | |
|     }
 | |
|   }
 | |
|   return const_param_idx = -1;
 | |
| }
 | |
| 
 | |
| std::vector<int> Type::get_all_param_values(int p) const {
 | |
|   if (p < 0 || p >= arity) {
 | |
|     return {};
 | |
|   }
 | |
|   std::vector<int> res;
 | |
|   res.reserve(constr_num);
 | |
|   for (const Constructor* cs : constructors) {
 | |
|     res.push_back(cs->param_const_val.at(p));
 | |
|   }
 | |
|   std::sort(res.begin(), res.end());
 | |
|   res.erase(std::unique(res.begin(), res.end()), res.end());
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| std::vector<int> Type::get_constr_by_param_value(int p, int pv) const {
 | |
|   std::vector<int> res;
 | |
|   if (p < 0 || p >= arity) {
 | |
|     return res;
 | |
|   }
 | |
|   for (int i = 0; i < constr_num; i++) {
 | |
|     if (constructors[i]->param_const_val[p] == pv) {
 | |
|       res.push_back(i);
 | |
|     }
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| void Type::compute_constructor_trie() {
 | |
|   if (cs_trie || !constr_num) {
 | |
|     return;
 | |
|   }
 | |
|   unsigned long long z = 1;
 | |
|   for (Constructor* cs : constructors) {
 | |
|     if (!z) {
 | |
|       throw src::ParseError{cs->where,
 | |
|                             std::string{"cannot work with more than 64 constructors for type `"} + get_name() + "`"};
 | |
|     }
 | |
|     cs_trie = BinTrie::insert_paths(std::move(cs_trie), cs->begins_with, z);
 | |
|     z <<= 1;
 | |
|   }
 | |
|   if (cs_trie) {
 | |
|     useful_depth = cs_trie->compute_useful_depth();
 | |
|     is_pfx_determ = !cs_trie->find_conflict_path();
 | |
|   } else {
 | |
|     useful_depth = 0;
 | |
|     is_pfx_determ = true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool Type::check_conflicts() {
 | |
|   compute_constructor_trie();
 | |
|   int cp = detect_const_params();
 | |
|   is_param_pfx_determ = is_param_determ = is_determ = true;
 | |
|   is_const_param_determ = is_const_param_pfx_determ = (cp >= 0);
 | |
|   if (!constr_num || !cs_trie) {
 | |
|     return false;
 | |
|   }
 | |
|   assert(constr_num <= 64);
 | |
|   // std::cerr << "prefix trie for constructors of `" << get_name() << "`" << std::endl;
 | |
|   // cs_trie->show(std::cerr);
 | |
|   ConflictGraph pfx_cg;
 | |
|   cs_trie->set_conflict_graph(pfx_cg);
 | |
|   for (int i = 0; i < constr_num; i++) {
 | |
|     AdmissibilityInfo& ap1 = constructors[i]->admissible_params;
 | |
|     for (int j = 0; j < i; j++) {
 | |
|       bool cp_same = (constructors[i]->get_const_param(cp) == constructors[j]->get_const_param(cp));
 | |
|       if (cp_same) {
 | |
|         is_const_param_determ = false;
 | |
|         if (pfx_cg[i][j]) {
 | |
|           is_const_param_pfx_determ = false;
 | |
|         }
 | |
|       }
 | |
|       if (ap1.conflicts_with(constructors[j]->admissible_params)) {
 | |
|         is_param_determ = false;
 | |
|         if (pfx_cg[i][j]) {
 | |
|           is_param_pfx_determ = false;
 | |
|           if (cp_same) {
 | |
|             conflict1 = j;
 | |
|             conflict2 = i;
 | |
|             is_determ = false;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return !is_determ;
 | |
| }
 | |
| 
 | |
| void Type::show_constructor_conflict() {
 | |
|   assert(cs_trie);
 | |
|   assert(conflict1 != conflict2);
 | |
|   int i = conflict1, j = conflict2;
 | |
|   assert(i >= 0 && i <= j && j < 64 && j < constr_num);
 | |
|   unsigned long long mask = (1ULL << i) | (1ULL << j);
 | |
|   unsigned long long pfx = cs_trie->find_conflict_path(0, mask);
 | |
|   assert(pfx);
 | |
|   ConflictSet cs_set{cs_trie->lookup_tag(pfx)};
 | |
|   std::cerr << "found conflict between constructors of type `" << get_name() << "`: prefix ";
 | |
|   show_tag(std::cerr, pfx);
 | |
|   AdmissibilityInfo& info1 = constructors[i]->admissible_params;
 | |
|   AdmissibilityInfo& info2 = constructors[j]->admissible_params;
 | |
|   bool need_params = !(info1.is_set_all() && info2.is_set_all());
 | |
|   int params = info1.conflicts_at(info2);
 | |
|   assert(params >= 0);
 | |
|   for (int s = 0; s < 64 && s < constr_num; s++) {
 | |
|     if (cs_set[s] &&
 | |
|         !(need_params ? constructors[s]->admissible_params[params] : constructors[s]->admissible_params.is_set_all())) {
 | |
|       cs_set.remove(s);
 | |
|     }
 | |
|   }
 | |
|   assert(cs_set[i] && cs_set[j]);
 | |
|   std::cerr << " can be present in " << cs_set.size() << " constructors:" << std::endl;
 | |
|   for (int s = 0; s < 64 && s < constr_num; s++) {
 | |
|     if (cs_set[s]) {
 | |
|       std::cerr << "\t";
 | |
|       constructors[s]->show(std::cerr);
 | |
|       std::cerr << std::endl;
 | |
|       constructors[s]->where.show_note("defined here");
 | |
|     }
 | |
|   }
 | |
|   if (need_params) {
 | |
|     std::cerr << "when type parameters are instantiated as " << get_name();
 | |
|     char nat = 'a', t = 'A';
 | |
|     for (int x : args) {
 | |
|       if (x & _IsNeg) {
 | |
|         std::cerr << " ~" << (x & _IsNat ? nat++ : t++);
 | |
|       } else if (x & _IsType) {
 | |
|         std::cerr << ' ' << t++;
 | |
|       } else {
 | |
|         std::cerr << ' ' << (params & 3);
 | |
|         if (params & 2) {
 | |
|           std::cerr << "+2*" << nat++;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     std::cerr << std::endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| int check_conflicts() {
 | |
|   int c = 0;
 | |
|   for (int i = builtin_types_num; i < types_num; i++) {
 | |
|     if (types[i].check_conflicts()) {
 | |
|       ++c;
 | |
|       types[i].show_constructor_conflict();
 | |
|     }
 | |
|   }
 | |
|   return c;
 | |
| }
 | |
| 
 | |
| void check_scheme() {
 | |
|   compute_admissible_params();
 | |
|   compute_begins_with();
 | |
|   compute_minmax_sizes();
 | |
|   compute_any_bits();
 | |
|   detect_basic_types();
 | |
|   if (show_size_warnings()) {
 | |
|     throw src::Fatal{"invalid scheme: some constructors or types cannot be instantiated or do not fit into cells"};
 | |
|   }
 | |
|   if (check_conflicts()) {
 | |
|     throw src::Fatal{"invalid scheme: have conflicts between constructors of some types"};
 | |
|   }
 | |
| }
 | |
| 
 | |
| void dump_all_types() {
 | |
|   std::cerr << types_num << " types defined, out of them " << builtin_types_num << " built-in, "
 | |
|             << types_num - builtin_types_num << " user-defined\n";
 | |
|   for (int i = 0; i < builtin_types_num; i++) {
 | |
|     Type& type = types[i];
 | |
|     if (type.used) {
 | |
|       std::cerr << "built-in type #" << i << ": `" << type.get_name() << "`, arity " << type.arity << "; prefixes "
 | |
|                 << type.begins_with << "; size " << type.size;
 | |
|       if (type.is_unit) {
 | |
|         std::cerr << " (UNIT)";
 | |
|       }
 | |
|       if (type.is_bool) {
 | |
|         std::cerr << " (BOOL)";
 | |
|       }
 | |
|       std::cerr << std::endl;
 | |
|     }
 | |
|   }
 | |
|   for (int i = builtin_types_num; i < types_num; i++) {
 | |
|     Type& type = types[i];
 | |
|     std::cerr << "type #" << i << ": `" << type.get_name() << "`, arity " << type.arity << ", " << type.constr_num
 | |
|               << " constructors\n";
 | |
|     if (type.const_param_idx >= 0) {
 | |
|       std::cerr << "  constant parameters:";
 | |
|       for (int j = 0; j < type.arity; j++) {
 | |
|         std::cerr << (type.is_const_arg(j) ? " const" : " *");
 | |
|       }
 | |
|       std::cerr << std::endl;
 | |
|     }
 | |
|     for (Constructor* cs : type.constructors) {
 | |
|       std::cerr << "  constructor `" << cs->get_name() << "`" << (cs->is_fwd ? " (simple forwarder)\n\t" : "\n\t");
 | |
|       cs->show(std::cerr);
 | |
|       std::cerr << "\n\tbegins with " << cs->begins_with << std::endl;
 | |
|       if (!cs->admissible_params.is_set_all()) {
 | |
|         std::cerr << "\tadmissibility " << cs->admissible_params << std::endl;
 | |
|       }
 | |
|       if (type.const_param_idx >= 0) {
 | |
|         std::cerr << "\tconstant parameter #" << type.const_param_idx + 1 << " = "
 | |
|                   << cs->get_const_param(type.const_param_idx) << std::endl;
 | |
|       }
 | |
|       std::cerr << "\tsize " << cs->size << (cs->has_fixed_size ? " (fixed)" : "")
 | |
|                 << (cs->any_bits ? " (any bits)" : "") << std::endl;
 | |
|       for (const Field& field : cs->fields) {
 | |
|         std::cerr << "\t\tfield `" << field.get_name() << "`: " << field.type << " (used=" << field.used
 | |
|                   << ") (is_nat_subtype=" << field.type->is_nat_subtype << ")\n";
 | |
|       }
 | |
|     }
 | |
|     if (type.is_unit) {
 | |
|       std::cerr << "  (UNIT)\n";
 | |
|     }
 | |
|     if (type.is_bool) {
 | |
|       std::cerr << "  (BOOL)\n";
 | |
|     }
 | |
|     if (type.is_enum) {
 | |
|       std::cerr << (type.is_simple_enum ? "  (SIMPLE ENUM)" : "  (ENUM)") << std::endl;
 | |
|     }
 | |
|     if (type.constr_num > 1) {
 | |
|       std::cerr << "  constructor detection: ";
 | |
|       if (type.is_pfx_determ) {
 | |
|         std::cerr << "PFX(" << type.useful_depth << ") ";
 | |
|       }
 | |
|       if (type.is_param_determ) {
 | |
|         std::cerr << "PARAM ";
 | |
|       }
 | |
|       if (type.is_const_param_determ) {
 | |
|         std::cerr << "CONST_PARAM ";
 | |
|       }
 | |
|       if (type.is_const_param_pfx_determ && !type.is_pfx_determ && !type.is_const_param_determ) {
 | |
|         std::cerr << "PFX(" << type.useful_depth << ")+CONST_PARAM ";
 | |
|       }
 | |
|       if (type.is_param_pfx_determ && !type.is_pfx_determ && !type.is_param_determ && !type.is_const_param_pfx_determ) {
 | |
|         std::cerr << "PFX(" << type.useful_depth << ")+PARAM ";
 | |
|       }
 | |
|       if (type.is_determ && !type.is_const_param_pfx_determ && !type.is_param_pfx_determ) {
 | |
|         std::cerr << "PFX(" << type.useful_depth << ")+CONST_PARAM+PARAM ";
 | |
|       }
 | |
|       if (!type.is_determ) {
 | |
|         std::cerr << "<CONFLICT>";
 | |
|       }
 | |
|       std::cerr << std::endl;
 | |
|     }
 | |
|     std::cerr << "  type size " << type.size << (type.has_fixed_size ? " (fixed)" : "")
 | |
|               << (type.any_bits ? " (any bits)" : "") << std::endl;
 | |
|     std::cerr << "  type begins with " << type.begins_with << std::endl;
 | |
|     if (!type.admissible_params.is_set_all()) {
 | |
|       std::cerr << "  type admissibility " << type.admissible_params << std::endl;
 | |
|     }
 | |
|     std::cerr << std::endl;
 | |
|     if (!type.constr_num && !type.is_final) {
 | |
|       sym::SymDef* sym_def = sym::lookup_symbol(type.type_name);
 | |
|       assert(sym_def);
 | |
|       throw src::ParseError{
 | |
|           sym_def ? sym_def->loc : src::SrcLocation{},
 | |
|           std::string{"implicitly defined type `"} + sym::symbols.get_name(type.type_name) + "` has no constructors"};
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void dump_all_constexpr() {
 | |
|   std::cerr << "****************\n" << const_type_expr_num << " constant expressions:\n";
 | |
|   for (int i = 1; i <= const_type_expr_num; i++) {
 | |
|     std::cerr << "expr #" << i << ": " << const_type_expr[i] << std::endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *   CODE GENERATION
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| std::vector<std::string> source_list;
 | |
| 
 | |
| void register_source(std::string source) {
 | |
|   source_list.push_back(source);
 | |
| }
 | |
| 
 | |
| }  // namespace tlbc
 | |
| 
 | |
| #include "tlbc-gen-cpp.cpp"
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *   TLBC MAIN
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| void usage(const char* progname) {
 | |
|   std::cerr << "usage: " << progname
 | |
|             << " [-v][-i][-h][-c][-z][-t][-T][-q][-n<namespace>][-o<output-filename>] {<tlb-filename> ...}\n"
 | |
|             << "-v\tIncrease verbosity level\n"
 | |
|             << "-t\tShow tag mismatch warnings\n"
 | |
|             << "-q\tOmit code generation (TLB scheme check only)\n"
 | |
|             << "-h\tGenerate C++ header file only (usually .h or .hpp)\n"
 | |
|             << "-c\tGenerate C++ source file only (usually .cpp)\n"
 | |
|             << "-T\tAdd type pointer members into generated C++ data record classes\n"
 | |
|             << "-z\tAppend .cpp or .hpp to output filename\n"
 | |
|             << "-n<namespace>\tPut generated code into specified namespace (default `tlb`)\n";
 | |
|   std::exit(2);
 | |
| }
 | |
| 
 | |
| std::string output_filename;
 | |
| 
 | |
| int main(int argc, char* const argv[]) {
 | |
|   int i;
 | |
|   bool interactive = false;
 | |
|   bool no_code_gen = false;
 | |
|   while ((i = getopt(argc, argv, "chin:o:qTtvz")) != -1) {
 | |
|     switch (i) {
 | |
|       case 'i':
 | |
|         interactive = true;
 | |
|         break;
 | |
|       case 'v':
 | |
|         ++verbosity;
 | |
|         break;
 | |
|       case 'o':
 | |
|         output_filename = optarg;
 | |
|         break;
 | |
|       case 'c':
 | |
|         tlbc::gen_cpp = true;
 | |
|         break;
 | |
|       case 'h':
 | |
|         tlbc::gen_hpp = true;
 | |
|         break;
 | |
|       case 'q':
 | |
|         no_code_gen = true;
 | |
|         break;
 | |
|       case 'n':
 | |
|         tlbc::cpp_namespace = optarg;
 | |
|         break;
 | |
|       case 'T':
 | |
|         tlbc::add_type_members = true;
 | |
|         break;
 | |
|       case 't':
 | |
|         tlbc::show_tag_warnings = true;
 | |
|         break;
 | |
|       case 'z':
 | |
|         tlbc::append_suffix = true;
 | |
|         break;
 | |
|       default:
 | |
|         usage(argv[0]);
 | |
|     }
 | |
|   }
 | |
|   if (verbosity >= 3) {
 | |
|     tlbc::show_tag_warnings = true;
 | |
|   }
 | |
| 
 | |
|   src::define_keywords();
 | |
|   tlbc::init_abstract_tables();
 | |
|   tlbc::define_builtins();
 | |
| 
 | |
|   int ok = 0, proc = 0;
 | |
|   try {
 | |
|     while (optind < argc) {
 | |
|       tlbc::register_source(argv[optind]);
 | |
|       ok += tlbc::parse_source_file(argv[optind++]);
 | |
|       proc++;
 | |
|     }
 | |
|     if (interactive) {
 | |
|       tlbc::register_source("");
 | |
|       ok += tlbc::parse_source_stdin();
 | |
|       proc++;
 | |
|     }
 | |
|     if (ok < proc) {
 | |
|       throw src::Fatal{"output code generation omitted because of errors"};
 | |
|     }
 | |
|     if (!proc) {
 | |
|       throw src::Fatal{"no source files, no output"};
 | |
|     }
 | |
|     tlbc::check_scheme();
 | |
|     if (verbosity > 0) {
 | |
|       tlbc::dump_all_types();
 | |
|       tlbc::dump_all_constexpr();
 | |
|     }
 | |
|     if (!no_code_gen) {
 | |
|       tlbc::init_forbidden_cpp_idents();
 | |
|       tlbc::generate_cpp_output(output_filename);
 | |
|     }
 | |
|   } catch (src::Fatal& fatal) {
 | |
|     std::cerr << "fatal: " << fatal << std::endl;
 | |
|     std::exit(1);
 | |
|   } catch (src::Error& error) {
 | |
|     std::cerr << error << std::endl;
 | |
|     std::exit(1);
 | |
|   }
 | |
| }
 |