2019-09-07 12:03:22 +02:00
|
|
|
/*
|
|
|
|
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-2019 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('?')
|
2019-09-18 19:46:32 +02:00
|
|
|
.add_kw_char('.')
|
2019-09-07 12:03:22 +02:00
|
|
|
.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] == '#') {
|
|
|
|
while (i < n) {
|
|
|
|
int c = str[i++];
|
|
|
|
if (c == '_') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
|
|
c -= '0';
|
|
|
|
} 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 == 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 {
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 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)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 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 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 TypeExpr{loc, tp, 0, {expr1, expr2}};
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeExpr* TypeExpr::mk_cellref(const src::SrcLocation& loc, TypeExpr* expr1) {
|
|
|
|
TypeExpr* expr = new 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 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;
|
|
|
|
}
|
2019-09-18 19:46:32 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-09-07 12:03:22 +02:00
|
|
|
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}};
|
2019-09-18 19:46:32 +02:00
|
|
|
unsigned char abstract_getbit_b_table[4][4] = {{1, 1, 1, 1}, {2, 1, 1, 1}, {1, 3, 3, 3}, {2, 3, 3, 3}};
|
2019-09-07 12:03:22 +02:00
|
|
|
|
|
|
|
unsigned char abstract_add_table[16][16];
|
|
|
|
unsigned char abstract_mul_table[16][16];
|
2019-09-18 19:46:32 +02:00
|
|
|
unsigned char abstract_getbit_table[16][16];
|
2019-09-07 12:03:22 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 19:46:32 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-07 12:03:22 +02:00
|
|
|
void init_abstract_tables() {
|
|
|
|
compute_semilat_table(abstract_add_table, abstract_add_base_table);
|
|
|
|
compute_semilat_table(abstract_mul_table, abstract_mul_base_table);
|
2019-09-18 19:46:32 +02:00
|
|
|
compute_semilat_b_table(abstract_getbit_table, abstract_getbit_b_table);
|
2019-09-07 12:03:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2019-09-18 19:46:32 +02:00
|
|
|
int abstract_getbit(int x, int y) {
|
|
|
|
return abstract_getbit_table[x & 15][y & 15];
|
|
|
|
}
|
|
|
|
|
2019-09-07 12:03:22 +02:00
|
|
|
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());
|
2019-09-18 19:46:32 +02:00
|
|
|
case te_GetBit:
|
|
|
|
assert(args.size() == 2);
|
|
|
|
return abstract_getbit(args[0]->abstract_interpret_nat(), args[1]->abstract_interpret_nat());
|
2019-09-07 12:03:22 +02:00
|
|
|
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;
|
2019-09-18 19:46:32 +02:00
|
|
|
case te_GetBit:
|
|
|
|
args[0]->const_type_name(os);
|
|
|
|
os << "_bit";
|
|
|
|
args[1]->const_type_name(os);
|
|
|
|
return;
|
2019-09-07 12:03:22 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-09-18 19:46:32 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-09-07 12:03:22 +02:00
|
|
|
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 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 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 19:46:32 +02:00
|
|
|
// 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;
|
|
|
|
}
|
2019-09-07 12:03:22 +02:00
|
|
|
|
2019-09-18 19:46:32 +02:00
|
|
|
// E ? E [ : E ]
|
2019-09-07 12:03:22 +02:00
|
|
|
TypeExpr* parse_expr95(Lexer& lex, Constructor& cs, int mode) {
|
2019-09-18 19:46:32 +02:00
|
|
|
TypeExpr* expr = parse_expr97(lex, cs, mode | 3);
|
2019-09-07 12:03:22 +02:00
|
|
|
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 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)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool parse_source(std::istream* is, const 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};
|
|
|
|
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() {
|
|
|
|
return parse_source(&std::cin, new src::FileDescr{"stdin", true});
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 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 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);
|
|
|
|
}
|
|
|
|
}
|