boom
This commit is contained in:
commit
ba458fffba
|
@ -0,0 +1,28 @@
|
|||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
BreakBeforeBraces: Attach
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
SpaceBeforeCtorInitializerColon: false
|
||||
SpaceBeforeInheritanceColon: false
|
||||
SpaceBeforeRangeBasedForLoopColon: true # Precede the colon with a space when and only when it is used as an operator
|
||||
SpaceAfterTemplateKeyword: true # To be determined
|
||||
ColumnLimit: 0
|
||||
UseTab: Never # To be determined
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
IndentCaseLabels: false
|
||||
SeparateDefinitionBlocks: Never
|
||||
EmptyLineBeforeAccessModifier: Never
|
||||
MaxEmptyLinesToKeep: 0
|
||||
InsertBraces: true
|
||||
DerivePointerAlignment: false
|
||||
ReferenceAlignment: Right
|
||||
PointerAlignment: Right
|
||||
QualifierAlignment: Custom
|
||||
QualifierOrder: ["static", "constexpr", "inline", "type", "const"]
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
LineEnding: LF
|
||||
InsertNewlineAtEOF: true
|
|
@ -0,0 +1 @@
|
|||
build/
|
|
@ -0,0 +1,38 @@
|
|||
CXX = clang++
|
||||
SOURCE = main.cpp
|
||||
NAME = $(basename $(SOURCE))
|
||||
BUILD_DIR = build
|
||||
VERSION = $(shell git describe --tags --always --dirty="-dev" 2>/dev/null || echo "unknown")
|
||||
MACHINE = $(shell $(CXX) -dumpmachine)
|
||||
STACK_SIZE = 4294967296
|
||||
CXXFLAGS = -std=c++20 -O3 -DSTACK_SIZE=$(STACK_SIZE)
|
||||
ifeq ($(findstring linux,$(MACHINE)), linux)
|
||||
EXE_EXT =
|
||||
LDFLAGS = -Wl,-z,stack-size=$(STACK_SIZE)
|
||||
else ifeq ($(findstring windows-msvc,$(MACHINE)), windows-msvc)
|
||||
EXE_EXT = .exe
|
||||
LDFLAGS = -Wl,/STACK:$(STACK_SIZE) -lUser32
|
||||
else ifeq ($(findstring windows-gnu,$(MACHINE)), windows-gnu)
|
||||
EXE_EXT = .exe
|
||||
LDFLAGS = -Wl,--stack,$(STACK_SIZE) -static
|
||||
else
|
||||
$(error Unsupported platform: $(MACHINE))
|
||||
endif
|
||||
ifeq ($(DEBUG), 1)
|
||||
CXXFLAGS += -g -fsanitize=address -fsanitize=undefined
|
||||
VERSION := $(VERSION)-debug
|
||||
endif
|
||||
ifeq ($(USE_GMP), 1)
|
||||
CXXFLAGS += -DUSE_GMP
|
||||
LDFLAGS += -lgmp
|
||||
TARGET = $(BUILD_DIR)/$(NAME)-$(VERSION)-$(MACHINE)-gmp$(EXE_EXT)
|
||||
else
|
||||
TARGET = $(BUILD_DIR)/$(NAME)-$(VERSION)-$(MACHINE)-nat$(EXE_EXT)
|
||||
endif
|
||||
.PHONY: all clean
|
||||
all: $(TARGET)
|
||||
$(TARGET): $(SOURCE)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS)
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
#include <gmpxx.h>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
class BigInt {
|
||||
mpz_class data;
|
||||
template <typename... T>
|
||||
BigInt(T &&...args):
|
||||
data(std::forward<T>(args)...) {}
|
||||
public:
|
||||
static BigInt from_string(std::string const &str) {
|
||||
return BigInt(str);
|
||||
}
|
||||
std::string to_string() const {
|
||||
return data.get_str();
|
||||
}
|
||||
operator bool() const {
|
||||
return data != 0;
|
||||
}
|
||||
friend BigInt operator+(BigInt const &lval, BigInt const &rval) {
|
||||
return BigInt(lval.data + rval.data);
|
||||
}
|
||||
friend BigInt operator-(BigInt const &lval, BigInt const &rval) {
|
||||
return BigInt(lval.data - rval.data);
|
||||
}
|
||||
friend BigInt operator*(BigInt const &lval, BigInt const &rval) {
|
||||
return BigInt(lval.data * rval.data);
|
||||
}
|
||||
friend BigInt operator/(BigInt const &lval, BigInt const &rval) {
|
||||
return BigInt(lval.data / rval.data);
|
||||
}
|
||||
friend BigInt operator%(BigInt const &lval, BigInt const &rval) {
|
||||
return BigInt(lval.data % rval.data);
|
||||
}
|
||||
friend bool operator>(BigInt const &lval, BigInt const &rval) {
|
||||
return lval.data > rval.data;
|
||||
}
|
||||
friend bool operator<(BigInt const &lval, BigInt const &rval) {
|
||||
return lval.data < rval.data;
|
||||
}
|
||||
friend bool operator>=(BigInt const &lval, BigInt const &rval) {
|
||||
return lval.data >= rval.data;
|
||||
}
|
||||
friend bool operator<=(BigInt const &lval, BigInt const &rval) {
|
||||
return lval.data <= rval.data;
|
||||
}
|
||||
friend bool operator==(BigInt const &lval, BigInt const &rval) {
|
||||
return lval.data == rval.data;
|
||||
}
|
||||
friend bool operator!=(BigInt const &lval, BigInt const &rval) {
|
||||
return lval.data != rval.data;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,232 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
class BigInt {
|
||||
size_t len;
|
||||
int8_t const *arr;
|
||||
size_t *ctr;
|
||||
int8_t get(size_t i) const {
|
||||
return arr[i < len ? i : len];
|
||||
}
|
||||
BigInt(size_t rlen, int8_t const *rarr):
|
||||
len(rlen), arr(rarr), ctr(new size_t(1)) {
|
||||
while (len && arr[len - 1] == arr[len]) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
public:
|
||||
static BigInt from_string(std::string_view sv) {
|
||||
auto itr = sv.rbegin(), end = sv.rend();
|
||||
if (sv.front() == '+' || sv.front() == '-') {
|
||||
end--;
|
||||
}
|
||||
size_t len = end - itr;
|
||||
int8_t *arr = new int8_t[len + 1];
|
||||
if (sv.front() == '-') {
|
||||
int8_t d = 10;
|
||||
for (size_t j = 0; itr != end; itr++) {
|
||||
if (*itr < '0' || *itr > '9') {
|
||||
delete[] arr;
|
||||
throw std::invalid_argument("invalid argument");
|
||||
} else {
|
||||
d = '9' - *itr + (d == 10);
|
||||
arr[j++] = d == 10 ? 0 : d;
|
||||
}
|
||||
}
|
||||
arr[len] = d == 10 ? 0 : 9;
|
||||
} else {
|
||||
for (size_t j = 0; itr != end; itr++) {
|
||||
if (*itr < '0' || *itr > '9') {
|
||||
delete[] arr;
|
||||
throw std::invalid_argument("invalid argument");
|
||||
} else {
|
||||
arr[j++] = *itr - '0';
|
||||
}
|
||||
}
|
||||
arr[len] = 0;
|
||||
}
|
||||
return BigInt(len, arr);
|
||||
}
|
||||
std::string to_string() const {
|
||||
char str[len + 2]; // GCC and Clang variable length array extension
|
||||
char *itr = &str[len + 2], *end = &str[len + 2];
|
||||
if (arr[len]) {
|
||||
bool flag = true;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
int8_t d = '9' - arr[i] + flag;
|
||||
*--itr = (flag = d > '9') ? '0' : d;
|
||||
}
|
||||
if (flag) {
|
||||
*--itr = '1';
|
||||
*--itr = '-';
|
||||
} else {
|
||||
*--itr = '-';
|
||||
}
|
||||
} else if (len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
*--itr = '0' + arr[i];
|
||||
}
|
||||
} else {
|
||||
*--itr = '0';
|
||||
}
|
||||
return std::string(itr, end);
|
||||
}
|
||||
BigInt(BigInt const &rval):
|
||||
len(rval.len), arr(rval.arr), ctr(rval.ctr) {
|
||||
++*ctr;
|
||||
}
|
||||
BigInt &operator=(BigInt const &rval) {
|
||||
++*rval.ctr;
|
||||
if (--*ctr == 0) {
|
||||
delete[] arr;
|
||||
delete ctr;
|
||||
}
|
||||
len = rval.len;
|
||||
arr = rval.arr;
|
||||
ctr = rval.ctr;
|
||||
return *this;
|
||||
}
|
||||
~BigInt() {
|
||||
if (--*ctr == 0) {
|
||||
delete[] arr;
|
||||
delete ctr;
|
||||
}
|
||||
}
|
||||
operator bool() const {
|
||||
return len || arr[len];
|
||||
}
|
||||
friend BigInt operator+(BigInt const &lbi, BigInt const &rbi) {
|
||||
size_t len = (lbi.len > rbi.len ? lbi.len : rbi.len) + 1;
|
||||
int8_t *arr = new int8_t[len + 1];
|
||||
int8_t s = 0;
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
s = lbi.get(i) + rbi.get(i) + (s >= 10);
|
||||
arr[i] = s >= 10 ? s - 10 : s;
|
||||
}
|
||||
return BigInt(len, arr);
|
||||
}
|
||||
friend BigInt operator-(BigInt const &lbi, BigInt const &rbi) {
|
||||
size_t len = (lbi.len > rbi.len ? lbi.len : rbi.len) + 1;
|
||||
int8_t *arr = new int8_t[len + 1];
|
||||
int8_t d = 0;
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
d = lbi.get(i) - rbi.get(i) - (d < 0);
|
||||
arr[i] = d < 0 ? d + 10 : d;
|
||||
}
|
||||
return BigInt(len, arr);
|
||||
}
|
||||
friend BigInt operator*(BigInt const &lbi, BigInt const &rbi) {
|
||||
size_t len = lbi.len + rbi.len + 1;
|
||||
int8_t *arr = new int8_t[len + 1]();
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
int8_t p = 0, s = 0;
|
||||
for (size_t j = 0; i + j <= len; j++) {
|
||||
p = lbi.get(j) * rbi.get(i) + p / 10;
|
||||
s = p % 10 + arr[i + j] + (s >= 10);
|
||||
arr[i + j] = s >= 10 ? s - 10 : s;
|
||||
}
|
||||
}
|
||||
return BigInt(len, arr);
|
||||
}
|
||||
template <bool select>
|
||||
friend BigInt divmod(BigInt const &lbi, BigInt const &rbi) {
|
||||
size_t len = lbi.len + rbi.len;
|
||||
int8_t *parr = new int8_t[len + 1], *narr = new int8_t[len + 1];
|
||||
int8_t *qarr = new int8_t[lbi.len + 1];
|
||||
int8_t *rarr = new int8_t[rbi.len + 1];
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
parr[i] = lbi.get(i);
|
||||
}
|
||||
if (lbi.arr[lbi.len] == rbi.arr[rbi.len]) {
|
||||
for (size_t i = lbi.len; i <= lbi.len; i--) {
|
||||
for (qarr[i] = 0;; qarr[i]++) {
|
||||
int8_t d = 0;
|
||||
for (size_t j = 0; i + j <= len; j++) {
|
||||
d = parr[i + j] - rbi.get(j) - (d < 0);
|
||||
narr[i + j] = d < 0 ? d + 10 : d;
|
||||
}
|
||||
if (narr[len] != parr[len]) {
|
||||
break;
|
||||
}
|
||||
for (size_t j = 0; i + j <= len; j++) {
|
||||
parr[i + j] = narr[i + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i <= rbi.len; i++) {
|
||||
rarr[i] = parr[i];
|
||||
}
|
||||
} else {
|
||||
for (size_t i = lbi.len; i <= lbi.len; i--) {
|
||||
for (qarr[i] = 9;; qarr[i]--) {
|
||||
int8_t s = 0;
|
||||
for (size_t j = 0; i + j <= len; j++) {
|
||||
s = parr[i + j] + rbi.get(j) + (s >= 10);
|
||||
narr[i + j] = s >= 10 ? s - 10 : s;
|
||||
}
|
||||
if (narr[len] != parr[len]) {
|
||||
break;
|
||||
}
|
||||
for (size_t j = 0; i + j <= len; j++) {
|
||||
parr[i + j] = narr[i + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i <= rbi.len; i++) {
|
||||
rarr[i] = narr[i];
|
||||
}
|
||||
}
|
||||
delete[] parr;
|
||||
delete[] narr;
|
||||
if constexpr (select) {
|
||||
delete[] qarr;
|
||||
return BigInt(rbi.len, rarr);
|
||||
} else {
|
||||
delete[] rarr;
|
||||
return BigInt(lbi.len, qarr);
|
||||
}
|
||||
}
|
||||
friend BigInt operator/(BigInt const &lbi, BigInt const &rbi) {
|
||||
return divmod<0>(lbi, rbi);
|
||||
}
|
||||
friend BigInt operator%(BigInt const &lbi, BigInt const &rbi) {
|
||||
return divmod<1>(lbi, rbi);
|
||||
}
|
||||
template <auto gt, auto eq, auto lt>
|
||||
friend auto compare(BigInt const &lbi, BigInt const &rbi) {
|
||||
if (lbi.arr[lbi.len] < rbi.arr[rbi.len]) {
|
||||
return gt;
|
||||
}
|
||||
if (lbi.arr[lbi.len] > rbi.arr[rbi.len]) {
|
||||
return lt;
|
||||
}
|
||||
for (size_t m = lbi.len > rbi.len ? lbi.len : rbi.len, i = m - 1; i < m; i--) {
|
||||
if (lbi.get(i) > rbi.get(i)) {
|
||||
return gt;
|
||||
}
|
||||
if (lbi.get(i) < rbi.get(i)) {
|
||||
return lt;
|
||||
}
|
||||
}
|
||||
return eq;
|
||||
}
|
||||
friend bool operator>(BigInt const &lbi, BigInt const &rbi) {
|
||||
return compare<true, false, false>(lbi, rbi);
|
||||
}
|
||||
friend bool operator<(BigInt const &lbi, BigInt const &rbi) {
|
||||
return compare<false, false, true>(lbi, rbi);
|
||||
}
|
||||
friend bool operator>=(BigInt const &lbi, BigInt const &rbi) {
|
||||
return compare<true, true, false>(lbi, rbi);
|
||||
}
|
||||
friend bool operator<=(BigInt const &lbi, BigInt const &rbi) {
|
||||
return compare<false, true, true>(lbi, rbi);
|
||||
}
|
||||
friend bool operator==(BigInt const &lbi, BigInt const &rbi) {
|
||||
return compare<false, true, false>(lbi, rbi);
|
||||
}
|
||||
friend bool operator!=(BigInt const &lbi, BigInt const &rbi) {
|
||||
return compare<true, false, true>(lbi, rbi);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
for f in *.cpp; do
|
||||
make SOURCE=$f
|
||||
make SOURCE=$f USE_GMP=1
|
||||
done
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
// The difference between Opt and Box is that Opt can be initialized or
|
||||
// assigned to nullptr, while Box can only be null after being moved.
|
||||
// Besides, the data in const Opt can still be modified, while the data
|
||||
// in const Box cannot be modified.
|
||||
// Both Opt and Box can be copied and moved. The data in them will also
|
||||
// be copied when they are copied.
|
||||
template <typename T>
|
||||
class Opt {
|
||||
T *data;
|
||||
Opt(T *data):
|
||||
data(data) {}
|
||||
public:
|
||||
Opt(std::nullptr_t):
|
||||
data(nullptr) {}
|
||||
Opt(Opt const &other):
|
||||
data(other.data ? new T(*other.data) : nullptr) {}
|
||||
Opt(Opt &&other):
|
||||
data(other.data) {
|
||||
other.data = nullptr;
|
||||
}
|
||||
Opt &operator=(std::nullptr_t) {
|
||||
delete data;
|
||||
data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
Opt &operator=(Opt const &other) {
|
||||
if (this != &other) {
|
||||
delete data;
|
||||
data = other.data ? new T(*other.data) : nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Opt &operator=(Opt &&other) {
|
||||
assert(this != &other); // self-assignment is not allowed
|
||||
delete data;
|
||||
data = other.data;
|
||||
other.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~Opt() {
|
||||
delete data;
|
||||
}
|
||||
template <typename... Args>
|
||||
static Opt make(Args &&...args) {
|
||||
return Opt(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
T &operator*() const & {
|
||||
return *data;
|
||||
}
|
||||
T *operator->() const & {
|
||||
return data;
|
||||
}
|
||||
operator bool() const {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
class Box {
|
||||
T *data;
|
||||
Box(T *data):
|
||||
data(data) {}
|
||||
public:
|
||||
Box(Box const &other):
|
||||
data(new T(*other.data)) {}
|
||||
Box(Box &&other):
|
||||
data(other.data) {
|
||||
other.data = nullptr;
|
||||
}
|
||||
Box &operator=(Box const &other) {
|
||||
if (this != &other) {
|
||||
delete data;
|
||||
data = new T(*other.data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Box &operator=(Box &&other) {
|
||||
assert(this != &other); // self-assignment is not allowed
|
||||
delete data;
|
||||
data = other.data;
|
||||
other.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~Box() {
|
||||
delete data;
|
||||
}
|
||||
template <typename... Args>
|
||||
static Box make(Args &&...args) {
|
||||
return Box(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
T const &operator*() const & {
|
||||
return *data;
|
||||
}
|
||||
T const *operator->() const & {
|
||||
return data;
|
||||
}
|
||||
T &operator*() & {
|
||||
return *data;
|
||||
}
|
||||
T *operator->() & {
|
||||
return data;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import sys
|
||||
import subprocess
|
||||
def bf2instrs(bf, pf, ml = False):
|
||||
ind, sp, end = (' ', '\n', '\n') if ml else ('', ' ', '')
|
||||
cnt = 0
|
||||
res = ''
|
||||
for ch in bf:
|
||||
if ch in '><+-.,':
|
||||
res += f'{cnt * ind}{pf}Instrs ({pf}{ch}) $ |{sp}'
|
||||
if ch == '[':
|
||||
res += f'{cnt * ind}{pf}Instrs ({pf}[] $ |{sp}'
|
||||
cnt += 1
|
||||
if ch == ']':
|
||||
res += f'{cnt * ind}{pf}Instop) $ |{sp}'
|
||||
cnt -= 1
|
||||
res += f'{cnt * ind}{pf}Instop{end}'
|
||||
assert cnt == 0
|
||||
return res
|
||||
def enc(s):
|
||||
return ''.join(f'\op $op {ord(c)} ' for c in s) + '...'
|
||||
def dec(l):
|
||||
return ''.join(chr(int(x)) for x in l.split(' ')[-2::-3])
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('input', nargs = '?', type = argparse.FileType('r'), default = sys.stdin)
|
||||
ap.add_argument('-o', '--output', type = argparse.FileType('w'), default = sys.stdout)
|
||||
eg = ap.add_mutually_exclusive_group(required = True)
|
||||
eg.add_argument('-v', '--cbv', action = 'store_const', const = '&', dest = 'prefix')
|
||||
eg.add_argument('-n', '--cbn', action = 'store_const', const = '$', dest = 'prefix')
|
||||
args = ap.parse_args()
|
||||
args.output.write(bf2instrs(args.input.read(), args.prefix, True))
|
||||
def test():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('bf', type = argparse.FileType('r'))
|
||||
ap.add_argument('-i', '--input', type = argparse.FileType('r'), default = sys.stdin)
|
||||
ap.add_argument('-o', '--output', type = argparse.FileType('w'), default = sys.stdout)
|
||||
eg = ap.add_mutually_exclusive_group(required = True)
|
||||
eg.add_argument('-v', '--cbv', action = 'store_const', const = 'cbv', dest = 'choice')
|
||||
eg.add_argument('-n', '--cbn', action = 'store_const', const = 'cbn', dest = 'choice')
|
||||
args = ap.parse_args()
|
||||
pf = {'cbv': '&', 'cbn': '$'}[args.choice]
|
||||
l = open(f'bf_{args.choice}.λ').read()
|
||||
l += f':fun {bf2instrs(args.bf.read(), pf)}\n'
|
||||
l += f':input {enc(args.input.read())}\n'
|
||||
l += f'cal {pf}run {pf}fun {pf}input\n'
|
||||
args.output.write(dec(subprocess.check_output([f'../build/lambda_{args.choice}'], input = l.encode()).decode()))
|
||||
if __name__ == '__main__':
|
||||
test()
|
|
@ -0,0 +1,20 @@
|
|||
:Mem ^cur ^lft ^rgt \op $op $cur $lft $rgt
|
||||
:State ^mem ^in ^out \op $op $mem $in $out
|
||||
:Cons ^head ^tail \op $op $head $tail
|
||||
:Instrs ^head ^tail \df \op $op $head $tail
|
||||
:Instop \df \op $df
|
||||
|
||||
:Y \g (\x $x $x) \x $g ($x $x)
|
||||
:fold $Y \fold \instrs \state $instrs $state \xhead \xtail $fold $xtail ($xhead $state)
|
||||
:succ \n = 255 $n 0 (+ 1 $n)
|
||||
:pred \n = 0 $n 255 (- 1 $n)
|
||||
:< \state $state \mem \in \out $mem \cur \lft \rgt $lft \lhead \ltail $State ($Mem $lhead $ltail ($Cons $cur $rgt)) $in $out
|
||||
:> \state $state \mem \in \out $mem \cur \lft \rgt $rgt \rhead \rtail $State ($Mem $rhead ($Cons $cur $lft) $rtail) $in $out
|
||||
:+ \state $state \mem \in \out $mem \cur \lft \rgt $State ($Mem ($succ $cur) $lft $rgt) $in $out
|
||||
:- \state $state \mem \in \out $mem \cur \lft \rgt $State ($Mem ($pred $cur) $lft $rgt) $in $out
|
||||
:. \state $state \mem \in \out $mem \cur \lft \rgt $State $mem $in ($Cons $cur $out)
|
||||
:, \state $state \mem \in \out $mem \cur \lft \rgt $in \ihead \itail $State ($Mem $ihead $lft $rgt) $itail $out
|
||||
:[] \instrs ($Y \fp \state $state \mem \in \out $mem \cur \lft \rgt = 0 $cur $state ($fp ($fun $state))) |fun $fold $instrs
|
||||
|
||||
:inf $Y \inf \op $op 0 $inf
|
||||
:run \instrs \in ($fold $instrs ($State ($Mem 0 $inf $inf) $in ...)) \mem \in \out $out
|
|
@ -0,0 +1,20 @@
|
|||
:Mem ^cur ^lft ^rgt \op $op $cur $lft $rgt
|
||||
:State ^mem ^in ^out \op $op $mem $in $out
|
||||
:Cons ^head ^tail \op $op $head $tail
|
||||
:Instrs ^head ^tail \df \op $op $head $tail
|
||||
:Instop \df \op $df
|
||||
|
||||
:Y \g (\x $x $x) \x $g ($x $x)
|
||||
:fold \instrs \state $instrs $state \xhead \xtail &fold $xtail ($xhead $state)
|
||||
:succ \n = 255 $n 0 (+ 1 $n)
|
||||
:pred \n = 0 $n 255 (- 1 $n)
|
||||
:< \state $state \mem \in \out $mem \cur \lft \rgt $lft \lhead \ltail &State (&Mem $lhead $ltail (&Cons $cur $rgt)) $in $out
|
||||
:> \state $state \mem \in \out $mem \cur \lft \rgt $rgt \rhead \rtail &State (&Mem $rhead (&Cons $cur $lft) $rtail) $in $out
|
||||
:+ \state $state \mem \in \out $mem \cur \lft \rgt &State (&Mem (&succ $cur) $lft $rgt) $in $out
|
||||
:- \state $state \mem \in \out $mem \cur \lft \rgt &State (&Mem (&pred $cur) $lft $rgt) $in $out
|
||||
:. \state $state \mem \in \out $mem \cur \lft \rgt &State $mem $in (&Cons $cur $out)
|
||||
:, \state $state \mem \in \out $mem \cur \lft \rgt $in \ihead \itail &State (&Mem $ihead $lft $rgt) $itail $out
|
||||
:[] \instrs (&Y \fp \state $state \mem \in \out $mem \cur \lft \rgt = 0 $cur $state ($fp ($fun $state))) |fun &fold $instrs
|
||||
|
||||
:inf \op $op 0 &inf
|
||||
:run \instrs \in (&fold $instrs (&State (&Mem 0 &inf &inf) $in ...)) \mem \in \out $out
|
|
@ -0,0 +1,54 @@
|
|||
# Y combinator
|
||||
:Y \g $x $x |x \x $g ($x $x)
|
||||
|
||||
# True and false
|
||||
:T \T \F $T
|
||||
:F \T \F $F
|
||||
|
||||
# Logical operators (Id, Not, And, Or, Xor)
|
||||
:= \c $c $T $F
|
||||
:~ \c $c $F $T
|
||||
:& \c \d $c $d $F
|
||||
:| \c \d $c $T $d
|
||||
:^ \c \d $c $= $~ $d
|
||||
|
||||
# List operators (Cons, Car, Cdr, Drop, Map, Scan, Filter, ZipWith)
|
||||
:con \a \b \o $o $a $b
|
||||
:car \l $l \a \_ $a
|
||||
:cdr \l $l \_ \d $d
|
||||
:drp $Y \z \n \l = 0 $n $l ($l \_ \d $z (- 1 $n) $d)
|
||||
:map \f $Y \z \l $l \a \d $con ($f $a) ($z $d)
|
||||
:scn \f $Y \z \x \l $l \a \d $con $x ($z ($f $x $a) $d)
|
||||
:flt \c $Y \z \l $l \a \d $c $a ($con $a ($z $d)) ($z $d)
|
||||
:zpw \f $Y \z \l \m $l \a \b $m \c \d $con ($f $a $c) ($z $b $d)
|
||||
|
||||
# Non-lazy list constructor
|
||||
:Nil ...
|
||||
:Con ^a ^b \o $o $a $b
|
||||
|
||||
# Evaluate the first n elements of a list
|
||||
:Evl $Y \z \n \l = 0 $n $Nil ($l \a \d $Con $a ($z (- 1 $n) $d))
|
||||
|
||||
# Three different implementations of the Fibonacci sequence
|
||||
:fib $Y \z \a \b $con $a ($z $b (+ $a $b))
|
||||
cal $Evl 1024 $ | $fib 0 1
|
||||
:fib \a \b $Y \l $con $a ($scn + $b $l)
|
||||
cal $Evl 1024 $ | $fib 0 1
|
||||
:fib \a \b $Y \l $con $a ($con $b ($zpw + $l ($cdr $l)))
|
||||
cal $Evl 1024 $ | $fib 0 1
|
||||
|
||||
# 2D space vector operations (Non-lazy construct, Add, Multiply)
|
||||
:Vec ^x ^y \o $o $x $y
|
||||
:add \u \v $u \a \b $v \c \d $Vec (+ $a $c) (+ $b $d)
|
||||
:mul \u \v $u \a \b $v \c \d $Vec (- (* $b $d) (* $a $c)) (+ (* $a $d) (* $b $c))
|
||||
|
||||
# Regular paper folding sequence
|
||||
:lft $Vec 0 (- 1 0)
|
||||
:rgt $Vec 0 (+ 1 0)
|
||||
:ins $Y \z \r $con $lft ($r \a \p $con $a ($con $rgt ($p \b \q $con $b ($z $q))))
|
||||
:rpf $Y \z $ins $z
|
||||
|
||||
# Generate first 1024 points on the dragon curve from the regular paper folding sequence
|
||||
:dir $scn $mul ($Vec 1 0) $rpf
|
||||
:pos $scn $add ($Vec 0 0) $dir
|
||||
cal $Evl 1024 $pos
|
|
@ -0,0 +1,54 @@
|
|||
# Y combinator
|
||||
:Y \g $g ($x $x) |x \x $g ($x $x)
|
||||
|
||||
# True and false
|
||||
:T \T \F $T
|
||||
:F \T \F $F
|
||||
|
||||
# Logical operators (Id, Not, And, Or, Xor)
|
||||
:= \c $c &T &F
|
||||
:~ \c $c &F &T
|
||||
:& \c \d $c $d &F
|
||||
:| \c \d $c &T $d
|
||||
:^ \c \d $c &= &~ $d
|
||||
|
||||
# List operators (Cons, Car, Cdr, Drop, Map, Scan, Filter, ZipWith)
|
||||
:con \a \b \o $o $a $b
|
||||
:car \l $l \a \_ $a
|
||||
:cdr \l $l \_ \d $d
|
||||
:drp \n \l = 0 $n $l ($l \_ \d &drp (- 1 $n) $d)
|
||||
:map \f \l $l \a \d &con ($f $a) (&map $f $d)
|
||||
:scn \f \x \l $l \a \d &con $x (&scn $f ($f $x $a) $d)
|
||||
:flt \c \l $l \a \d $c $a (&con $a (&flt $c $d)) (&flt $c $d)
|
||||
:zpw \f \l \m $l \a \b $m \c \d &con ($f $a $c) (&zpw $f $b $d)
|
||||
|
||||
# Non-lazy list constructor
|
||||
:Nil ...
|
||||
:Con ^a ^b \o $o $a $b
|
||||
|
||||
# Evaluate the first n elements of a list
|
||||
:Evl \n \l = 0 $n &Nil ($l \a \d &Con $a (&Evl (- 1 $n) $d))
|
||||
|
||||
# Three different implementations of the Fibonacci sequence
|
||||
:fib \a \b &con $a (&fib $b (+ $a $b))
|
||||
cal &Evl 1024 $ | &fib 0 1
|
||||
:fib \a \b &Y \l &con $a (&scn + $b $l)
|
||||
cal &Evl 1024 $ | &fib 0 1
|
||||
:fib \a \b &Y \l &con $a (&con $b (&zpw + $l (&cdr $l)))
|
||||
cal &Evl 1024 $ | &fib 0 1
|
||||
|
||||
# 2D space vector operations (Non-lazy construct, Add, Multiply)
|
||||
:Vec ^x ^y \o $o $x $y
|
||||
:add \u \v $u \a \b $v \c \d &Vec (+ $a $c) (+ $b $d)
|
||||
:mul \u \v $u \a \b $v \c \d &Vec (- (* $b $d) (* $a $c)) (+ (* $a $d) (* $b $c))
|
||||
|
||||
# Regular paper folding sequence
|
||||
:lft &Vec 0 (- 1 0)
|
||||
:rgt &Vec 0 (+ 1 0)
|
||||
:ins \r &con &lft ($r \a \p &con $a (&con &rgt ($p \b \q &con $b (&ins $q))))
|
||||
:rpf &ins &rpf
|
||||
|
||||
# Generate first 1024 points on the dragon curve from the regular paper folding sequence
|
||||
:dir &scn &mul (&Vec 1 0) &rpf
|
||||
:pos &scn &add (&Vec 0 0) &dir
|
||||
cal &Evl 1024 &pos
|
|
@ -0,0 +1,471 @@
|
|||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "container.hpp"
|
||||
#include "slice.hpp"
|
||||
#ifndef USE_GMP
|
||||
#include "bigint_nat.hpp" // native big integer
|
||||
#else
|
||||
#include "bigint_gmp.hpp" // GNU MP big integer
|
||||
#endif
|
||||
#if defined _WIN32
|
||||
#include <Windows.h>
|
||||
#elif defined __unix__
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#ifndef STACK_SIZE
|
||||
#define STACK_SIZE 8388608 // 8 MiB
|
||||
#endif
|
||||
char *stack_top;
|
||||
char *stack_cur;
|
||||
void ini_stack() {
|
||||
char dummy;
|
||||
stack_top = &dummy;
|
||||
}
|
||||
bool chk_stack() {
|
||||
char dummy;
|
||||
stack_cur = &dummy;
|
||||
return stack_top - stack_cur >= STACK_SIZE / 2;
|
||||
}
|
||||
#if defined _WIN32
|
||||
void clr_flag() {
|
||||
GetAsyncKeyState(VK_ESCAPE);
|
||||
}
|
||||
bool chk_flag() {
|
||||
return GetAsyncKeyState(VK_ESCAPE) & 0x0001;
|
||||
}
|
||||
#elif defined __unix__
|
||||
bool flag_rec;
|
||||
void set_flag(int) {
|
||||
flag_rec = 1;
|
||||
}
|
||||
void clr_flag() {
|
||||
flag_rec = 0;
|
||||
}
|
||||
bool chk_flag() {
|
||||
return flag_rec;
|
||||
}
|
||||
#endif
|
||||
auto read(Slice &exp) {
|
||||
auto i = exp.get_beg();
|
||||
auto n = exp.get_end();
|
||||
for (;; i++) {
|
||||
if (i == n) {
|
||||
return exp.reset_to(i, n), exp.from_to(i, i);
|
||||
} else if (*i != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto j = i;
|
||||
auto c = 0;
|
||||
for (;; i++) {
|
||||
if ((i == n || *i == ' ') && c == 0) {
|
||||
return exp.reset_to(i, n), exp.from_to(j, i);
|
||||
} else if (i == n) {
|
||||
throw std::runtime_error("mismatched parentheses");
|
||||
} else if (*i == '(') {
|
||||
c++;
|
||||
} else if (*i == ')') {
|
||||
c--;
|
||||
}
|
||||
}
|
||||
}
|
||||
BigInt operator+(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator-(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator*(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator/(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator%(BigInt const &lval, BigInt const &rval);
|
||||
bool operator>(BigInt const &lval, BigInt const &rval);
|
||||
bool operator<(BigInt const &lval, BigInt const &rval);
|
||||
bool operator>=(BigInt const &lval, BigInt const &rval);
|
||||
bool operator<=(BigInt const &lval, BigInt const &rval);
|
||||
bool operator==(BigInt const &lval, BigInt const &rval);
|
||||
bool operator!=(BigInt const &lval, BigInt const &rval);
|
||||
typedef BigInt (*opr_t)(BigInt const &, BigInt const &);
|
||||
typedef bool (*cmp_t)(BigInt const &, BigInt const &);
|
||||
static inline std::unordered_map<char, opr_t> const oprs = {
|
||||
{'+', operator+},
|
||||
{'-', operator-},
|
||||
{'*', operator*},
|
||||
{'/', operator/},
|
||||
{'%', operator%},
|
||||
};
|
||||
static inline std::unordered_map<char, cmp_t> const cmps = {
|
||||
{'>', operator>},
|
||||
{'<', operator<},
|
||||
{'=', operator==},
|
||||
};
|
||||
class Tree {
|
||||
enum TokenIdx: std::size_t {
|
||||
Und,
|
||||
Nil, Chk,
|
||||
Par, Int,
|
||||
Opr, AOI,
|
||||
Cmp, ACI,
|
||||
LEF, EEF, // Lazy/Eager-Evaluation Function
|
||||
App, Arg,
|
||||
};
|
||||
using TokenVar = std::variant<
|
||||
std::nullopt_t,
|
||||
std::monostate, std::monostate,
|
||||
std::string, BigInt,
|
||||
std::pair<char, opr_t>, std::pair<std::pair<char, opr_t>, BigInt>,
|
||||
std::pair<char, cmp_t>, std::pair<std::pair<char, cmp_t>, BigInt>,
|
||||
Box<std::pair<std::string, Tree>>, Box<std::pair<std::string, Tree>>,
|
||||
Box<std::pair<Tree, Tree>>, std::shared_ptr<std::pair<Tree, bool>>>;
|
||||
TokenVar token;
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<TokenVar, Args &&...>>>
|
||||
Tree(Args &&...args): token(std::forward<Args>(args)...) {}
|
||||
static Tree first(Tree &&fst) {
|
||||
if (fst.token.index() == TokenIdx::Und) {
|
||||
throw std::runtime_error("empty expression");
|
||||
} else {
|
||||
return std::move(fst);
|
||||
}
|
||||
}
|
||||
static Tree build(Tree &&fst, Tree &&snd) {
|
||||
if (fst.token.index() == TokenIdx::Und) {
|
||||
return std::move(snd);
|
||||
} else {
|
||||
return Box<std::pair<Tree, Tree>>::make(std::move(fst), std::move(snd));
|
||||
}
|
||||
}
|
||||
static Tree parse(Slice &&exp, Tree &&fun = std::nullopt, Tree &&fst = std::nullopt) {
|
||||
if (auto sym = read(exp); sym.empty()) {
|
||||
return build(std::move(fun), first(std::move(fst)));
|
||||
} else if (sym[0] == '\\') {
|
||||
return build(std::move(fun), build(std::move(fst), Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), parse(std::move(exp))))));
|
||||
} else if (sym[0] == '|') {
|
||||
return parse(std::move(exp), Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), build(std::move(fun), first(std::move(fst))))));
|
||||
} else if (sym[0] == '^') {
|
||||
return build(std::move(fun), build(std::move(fst), Tree(std::in_place_index<TokenIdx::EEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), parse(std::move(exp))))));
|
||||
} else if (sym[0] == '@') {
|
||||
return parse(std::move(exp), Tree(std::in_place_index<TokenIdx::EEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), build(std::move(fun), first(std::move(fst))))));
|
||||
} else {
|
||||
return parse(std::move(exp), std::move(fun), build(std::move(fst), lex(std::move(sym))));
|
||||
}
|
||||
}
|
||||
static Tree lex(Slice const &sym) {
|
||||
if (sym[0] == '(' && sym[-1] == ')') {
|
||||
return parse(sym(1, -1));
|
||||
} else if (sym[0] == '$') {
|
||||
return Tree(std::in_place_index<TokenIdx::Par>, sym(1, 0));
|
||||
} else if (sym.size() == 3 && sym == "...") {
|
||||
return Tree(std::in_place_index<TokenIdx::Nil>);
|
||||
} else if (sym.size() == 1 && sym == "?") {
|
||||
return Tree(std::in_place_index<TokenIdx::Chk>);
|
||||
} else if (auto const &o = oprs.find(sym[0]); sym.size() == 1 && o != oprs.end()) {
|
||||
return Tree(std::in_place_index<TokenIdx::Opr>, *o);
|
||||
} else if (auto const &c = cmps.find(sym[0]); sym.size() == 1 && c != cmps.end()) {
|
||||
return Tree(std::in_place_index<TokenIdx::Cmp>, *c);
|
||||
} else try {
|
||||
return Tree(std::in_place_index<TokenIdx::Int>, BigInt::from_string(sym));
|
||||
} catch (...) {
|
||||
throw std::runtime_error("invalid symbol: " + std::string(sym));
|
||||
}
|
||||
}
|
||||
void calc() {
|
||||
static const auto T = Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("T", Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("F", Tree(std::in_place_index<TokenIdx::Par>, "T")))));
|
||||
static const auto F = Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("T", Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("F", Tree(std::in_place_index<TokenIdx::Par>, "F")))));
|
||||
if (chk_stack()) {
|
||||
throw std::runtime_error("recursion limit exceeded");
|
||||
}
|
||||
tail_call:
|
||||
if (chk_flag()) {
|
||||
throw std::runtime_error("keyboard interrupt");
|
||||
}
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
fst.calc();
|
||||
if (auto pnil = std::get_if<TokenIdx::Nil>(&fst.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else if (auto pchk = std::get_if<TokenIdx::Chk>(&fst.token)) {
|
||||
snd.calc();
|
||||
*this = snd.token.index() == TokenIdx::Nil ? F : T;
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&fst.token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
tmp.substitute(std::make_shared<std::pair<Tree, bool>>(std::move(snd), 0), std::move(par));
|
||||
*this = Tree(std::move(tmp));
|
||||
goto tail_call;
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&fst.token)) {
|
||||
snd.calc();
|
||||
auto &[par, tmp] = **peef;
|
||||
tmp.substitute(std::make_shared<std::pair<Tree, bool>>(std::move(snd), 1), std::move(par));
|
||||
*this = Tree(std::move(tmp));
|
||||
goto tail_call;
|
||||
} else if (auto popr = std::get_if<TokenIdx::Opr>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token); pint && (*pint || popr->first != '/' && popr->first != '%')) {
|
||||
token = std::make_pair(std::move(*popr), std::move(*pint));
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto pcmp = std::get_if<TokenIdx::Cmp>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token)) {
|
||||
token = std::make_pair(std::move(*pcmp), std::move(*pint));
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto paoi = std::get_if<TokenIdx::AOI>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token)) {
|
||||
token = paoi->first.second(std::move(*pint), std::move(paoi->second));
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto paci = std::get_if<TokenIdx::ACI>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token)) {
|
||||
*this = paci->first.second(std::move(*pint), std::move(paci->second)) ? T : F;
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("invalid function: " + fst.translate());
|
||||
}
|
||||
} else if (auto parg = std::get_if<TokenIdx::Arg>(&token)) {
|
||||
auto &shr = (*parg)->first;
|
||||
auto rec = (*parg)->second;
|
||||
if (parg->use_count() == 1) {
|
||||
*this = Tree(std::move(shr));
|
||||
if (not rec) {
|
||||
goto tail_call;
|
||||
}
|
||||
} else {
|
||||
if (not rec) {
|
||||
shr.calc();
|
||||
rec = true;
|
||||
}
|
||||
*this = shr;
|
||||
}
|
||||
}
|
||||
}
|
||||
void substitute(std::shared_ptr<std::pair<Tree, bool>> const &arg, std::string const &tar) {
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
fst.substitute(arg, tar);
|
||||
snd.substitute(arg, tar);
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
if (par != tar) {
|
||||
tmp.substitute(arg, tar);
|
||||
}
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **peef;
|
||||
if (par != tar) {
|
||||
tmp.substitute(arg, tar);
|
||||
}
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(&token)) {
|
||||
if (*ppar == tar) {
|
||||
token.emplace<TokenIdx::Arg>(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
void analyze(std::unordered_set<std::string> &set) {
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
fst.analyze(set);
|
||||
snd.analyze(set);
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
if (set.find(par) != set.end()) {
|
||||
tmp.analyze(set);
|
||||
} else {
|
||||
auto pos = set.insert(par).first;
|
||||
tmp.analyze(set);
|
||||
set.erase(pos);
|
||||
}
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **peef;
|
||||
if (set.find(par) != set.end()) {
|
||||
tmp.analyze(set);
|
||||
} else {
|
||||
auto pos = set.insert(par).first;
|
||||
tmp.analyze(set);
|
||||
set.erase(pos);
|
||||
}
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(&token)) {
|
||||
if (set.find(*ppar) == set.end()) {
|
||||
if (auto const &itr = map.find(*ppar); itr != map.end()) {
|
||||
token.emplace<TokenIdx::Arg>(itr->second);
|
||||
} else {
|
||||
throw std::runtime_error("unbound variable: $" + *ppar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline std::unordered_map<std::string, std::shared_ptr<std::pair<Tree, bool>>> map;
|
||||
public:
|
||||
Tree(Tree const &other): token(other.token) {}
|
||||
Tree &operator=(Tree const &other) {
|
||||
token = other.token;
|
||||
return *this;
|
||||
}
|
||||
Tree(Tree &&other): token(std::exchange(other.token, std::nullopt)) {}
|
||||
Tree &operator=(Tree &&other) {
|
||||
token = std::exchange(other.token, std::nullopt);
|
||||
return *this;
|
||||
}
|
||||
~Tree() {
|
||||
if (token.index() == TokenIdx::Und) {
|
||||
return;
|
||||
}
|
||||
std::queue<TokenVar> flat;
|
||||
flat.push(std::exchange(token, std::nullopt));
|
||||
for (; not flat.empty(); flat.pop()) {
|
||||
auto &token = flat.front();
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
flat.push(std::exchange(fst.token, std::nullopt));
|
||||
flat.push(std::exchange(snd.token, std::nullopt));
|
||||
} else if (auto parg = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **parg;
|
||||
flat.push(std::exchange(tmp.token, std::nullopt));
|
||||
} else if (auto parg = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **parg;
|
||||
flat.push(std::exchange(tmp.token, std::nullopt));
|
||||
} else if (auto parg = std::get_if<TokenIdx::Arg>(&token)) {
|
||||
auto &shr = (*parg)->first;
|
||||
if (parg->use_count() == 1) {
|
||||
flat.push(std::exchange(shr.token, std::nullopt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static auto const &put(Slice &&exp, std::string const &par, bool calc) {
|
||||
auto res = parse(std::move(exp));
|
||||
std::unordered_set<std::string> set;
|
||||
res.analyze(set);
|
||||
map.erase(par);
|
||||
if (calc) {
|
||||
clr_flag();
|
||||
res.calc();
|
||||
}
|
||||
return map.emplace(par, std::make_shared<std::pair<Tree, bool>>(std::move(res), calc)).first->second->first;
|
||||
}
|
||||
static auto const &dir() {
|
||||
return map;
|
||||
}
|
||||
static void clr() {
|
||||
return map.clear();
|
||||
}
|
||||
std::string translate(bool lb = 0, bool rb = 0) const {
|
||||
if (auto pnil = std::get_if<TokenIdx::Nil>(&token)) {
|
||||
return "...";
|
||||
} else if (auto pchk = std::get_if<TokenIdx::Chk>(&token)) {
|
||||
return "?";
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
auto s = "\\" + par + " " + tmp.translate(0, rb && !rb);
|
||||
return rb ? "(" + s + ")" : s;
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **peef;
|
||||
auto s = "^" + par + " " + tmp.translate(0, rb && !rb);
|
||||
return rb ? "(" + s + ")" : s;
|
||||
} else if (auto pint = std::get_if<TokenIdx::Int>(&token)) {
|
||||
return pint->to_string();
|
||||
} else if (auto popr = std::get_if<TokenIdx::Opr>(&token)) {
|
||||
return std::string{popr->first};
|
||||
} else if (auto pcmp = std::get_if<TokenIdx::Cmp>(&token)) {
|
||||
return std::string{pcmp->first};
|
||||
} else if (auto paoi = std::get_if<TokenIdx::AOI>(&token)) {
|
||||
auto s = std::string{paoi->first.first, ' '} + paoi->second.to_string();
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto paci = std::get_if<TokenIdx::ACI>(&token)) {
|
||||
auto s = std::string{paci->first.first, ' '} + paci->second.to_string();
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
auto s = fst.translate(lb && !lb, 1) + " " + snd.translate(1, rb && !lb);
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto parg = std::get_if<TokenIdx::Arg>(&token)) {
|
||||
return (*parg)->first.translate(lb, rb);
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(&token)) {
|
||||
return "$" + *ppar;
|
||||
} else {
|
||||
assert(false); // unreachable
|
||||
}
|
||||
}
|
||||
};
|
||||
int main(int argc, char *argv[]) {
|
||||
ini_stack();
|
||||
bool check_stdin = false;
|
||||
bool check_stdout = false;
|
||||
bool check_stderr = false;
|
||||
#if defined _WIN32
|
||||
DWORD dwModeTemp;
|
||||
check_stdin = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwModeTemp);
|
||||
check_stdout = GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwModeTemp);
|
||||
check_stderr = GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &dwModeTemp);
|
||||
#elif defined __unix__
|
||||
check_stdin = isatty(fileno(stdin));
|
||||
check_stdout = isatty(fileno(stdout));
|
||||
check_stderr = isatty(fileno(stderr));
|
||||
struct rlimit rlim;
|
||||
getrlimit(RLIMIT_STACK, &rlim);
|
||||
rlim.rlim_cur = STACK_SIZE;
|
||||
setrlimit(RLIMIT_STACK, &rlim);
|
||||
struct sigaction act;
|
||||
act.sa_handler = set_flag;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0; // use SA_RESTART to avoid getting EOF when SIGINT is received during input
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
#endif
|
||||
std::string ps_in = check_stderr && check_stdin ? ">> " : "";
|
||||
std::string ps_out = check_stderr && check_stdout ? "=> " : "";
|
||||
std::string ps_res = check_stderr && check_stdout ? "== " : "";
|
||||
for (bool end = false; not end;) {
|
||||
std::cerr << ps_in;
|
||||
Slice exp = Slice::getline(std::cin);
|
||||
if (std::cin.eof()) {
|
||||
end = true;
|
||||
if (check_stderr && check_stdin) {
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (auto cmd = read(exp); cmd.empty() || cmd.size() == 1 && cmd == "#") {
|
||||
continue;
|
||||
} else if (cmd[0] == ':') {
|
||||
Tree::put(std::move(exp), cmd(1, 0), 0);
|
||||
} else if (cmd.size() == 3 && cmd == "cal") {
|
||||
auto &res = Tree::put(std::move(exp), "", 1);
|
||||
std::cerr << ps_res;
|
||||
std::cout << res.translate() << std::endl;
|
||||
} else if (cmd.size() == 3 && cmd == "dir") {
|
||||
for (auto &[par, arg] : Tree::dir()) {
|
||||
std::cerr << ps_out;
|
||||
std::cout << ":" + par << std::endl;
|
||||
}
|
||||
} else if (cmd.size() == 3 && cmd == "clr") {
|
||||
Tree::clr();
|
||||
} else if (cmd.size() == 3 && cmd == "end") {
|
||||
end = true;
|
||||
} else {
|
||||
throw std::runtime_error("unknown command: " + std::string(cmd));
|
||||
}
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << "Runtime Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,481 @@
|
|||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "container.hpp"
|
||||
#include "slice.hpp"
|
||||
#ifndef USE_GMP
|
||||
#include "bigint_nat.hpp" // native big integer
|
||||
#else
|
||||
#include "bigint_gmp.hpp" // GNU MP big integer
|
||||
#endif
|
||||
#if defined _WIN32
|
||||
#include <Windows.h>
|
||||
#elif defined __unix__
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#ifndef STACK_SIZE
|
||||
#define STACK_SIZE 8388608 // 8 MiB
|
||||
#endif
|
||||
char *stack_top;
|
||||
char *stack_cur;
|
||||
void ini_stack() {
|
||||
char dummy;
|
||||
stack_top = &dummy;
|
||||
}
|
||||
bool chk_stack() {
|
||||
char dummy;
|
||||
stack_cur = &dummy;
|
||||
return stack_top - stack_cur >= STACK_SIZE / 2;
|
||||
}
|
||||
#if defined _WIN32
|
||||
void clr_flag() {
|
||||
GetAsyncKeyState(VK_ESCAPE);
|
||||
}
|
||||
bool chk_flag() {
|
||||
return GetAsyncKeyState(VK_ESCAPE) & 0x0001;
|
||||
}
|
||||
#elif defined __unix__
|
||||
bool flag_rec;
|
||||
void set_flag(int) {
|
||||
flag_rec = 1;
|
||||
}
|
||||
void clr_flag() {
|
||||
flag_rec = 0;
|
||||
}
|
||||
bool chk_flag() {
|
||||
return flag_rec;
|
||||
}
|
||||
#endif
|
||||
auto read(Slice &exp) {
|
||||
auto i = exp.get_beg();
|
||||
auto n = exp.get_end();
|
||||
for (;; i++) {
|
||||
if (i == n) {
|
||||
return exp.reset_to(i, n), exp.from_to(i, i);
|
||||
} else if (*i != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto j = i;
|
||||
auto c = 0;
|
||||
for (;; i++) {
|
||||
if ((i == n || *i == ' ') && c == 0) {
|
||||
return exp.reset_to(i, n), exp.from_to(j, i);
|
||||
} else if (i == n) {
|
||||
throw std::runtime_error("mismatched parentheses");
|
||||
} else if (*i == '(') {
|
||||
c++;
|
||||
} else if (*i == ')') {
|
||||
c--;
|
||||
}
|
||||
}
|
||||
}
|
||||
BigInt operator+(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator-(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator*(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator/(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator%(BigInt const &lval, BigInt const &rval);
|
||||
bool operator>(BigInt const &lval, BigInt const &rval);
|
||||
bool operator<(BigInt const &lval, BigInt const &rval);
|
||||
bool operator>=(BigInt const &lval, BigInt const &rval);
|
||||
bool operator<=(BigInt const &lval, BigInt const &rval);
|
||||
bool operator==(BigInt const &lval, BigInt const &rval);
|
||||
bool operator!=(BigInt const &lval, BigInt const &rval);
|
||||
typedef BigInt (*opr_t)(BigInt const &, BigInt const &);
|
||||
typedef bool (*cmp_t)(BigInt const &, BigInt const &);
|
||||
static inline std::unordered_map<char, opr_t> const oprs = {
|
||||
{'+', operator+},
|
||||
{'-', operator-},
|
||||
{'*', operator*},
|
||||
{'/', operator/},
|
||||
{'%', operator%},
|
||||
};
|
||||
static inline std::unordered_map<char, cmp_t> const cmps = {
|
||||
{'>', operator>},
|
||||
{'<', operator<},
|
||||
{'=', operator==},
|
||||
};
|
||||
class Tree {
|
||||
enum TokenIdx: std::size_t {
|
||||
Und,
|
||||
Nil, Chk,
|
||||
Par, Int,
|
||||
Opr, AOI,
|
||||
Cmp, ACI,
|
||||
LEF, EEF, // Lazy/Eager-Evaluation Function
|
||||
App, Arg, Glb,
|
||||
};
|
||||
using TokenVar = std::variant<
|
||||
std::nullopt_t,
|
||||
std::monostate, std::monostate,
|
||||
std::string, BigInt,
|
||||
std::pair<char, opr_t>, std::pair<std::pair<char, opr_t>, BigInt>,
|
||||
std::pair<char, cmp_t>, std::pair<std::pair<char, cmp_t>, BigInt>,
|
||||
Box<std::pair<std::string, Tree>>, Box<std::pair<std::string, Tree>>,
|
||||
Box<std::pair<Tree, Tree>>, std::shared_ptr<std::pair<Tree, bool>>, std::string>;
|
||||
TokenVar token;
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<TokenVar, Args &&...>>>
|
||||
Tree(Args &&...args): token(std::forward<Args>(args)...) {}
|
||||
static Tree first(Tree &&fst) {
|
||||
if (fst.token.index() == TokenIdx::Und) {
|
||||
throw std::runtime_error("empty expression");
|
||||
} else {
|
||||
return std::move(fst);
|
||||
}
|
||||
}
|
||||
static Tree build(Tree &&fst, Tree &&snd) {
|
||||
if (fst.token.index() == TokenIdx::Und) {
|
||||
return std::move(snd);
|
||||
} else {
|
||||
return Box<std::pair<Tree, Tree>>::make(std::move(fst), std::move(snd));
|
||||
}
|
||||
}
|
||||
static Tree parse(Slice &&exp, Tree &&fun = std::nullopt, Tree &&fst = std::nullopt) {
|
||||
if (auto sym = read(exp); sym.empty()) {
|
||||
return build(std::move(fun), first(std::move(fst)));
|
||||
} else if (sym[0] == '\\') {
|
||||
return build(std::move(fun), build(std::move(fst), Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), parse(std::move(exp))))));
|
||||
} else if (sym[0] == '|') {
|
||||
return parse(std::move(exp), Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), build(std::move(fun), first(std::move(fst))))));
|
||||
} else if (sym[0] == '^') {
|
||||
return build(std::move(fun), build(std::move(fst), Tree(std::in_place_index<TokenIdx::EEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), parse(std::move(exp))))));
|
||||
} else if (sym[0] == '@') {
|
||||
return parse(std::move(exp), Tree(std::in_place_index<TokenIdx::EEF>, Box<std::pair<std::string, Tree>>::make(sym(1, 0), build(std::move(fun), first(std::move(fst))))));
|
||||
} else {
|
||||
return parse(std::move(exp), std::move(fun), build(std::move(fst), lex(std::move(sym))));
|
||||
}
|
||||
}
|
||||
static Tree lex(Slice const &sym) {
|
||||
if (sym[0] == '(' && sym[-1] == ')') {
|
||||
return parse(sym(1, -1));
|
||||
} else if (sym[0] == '$') {
|
||||
return Tree(std::in_place_index<TokenIdx::Par>, sym(1, 0));
|
||||
} else if (sym[0] == '&') {
|
||||
return Tree(std::in_place_index<TokenIdx::Glb>, sym(1, 0));
|
||||
} else if (sym.size() == 3 && sym == "...") {
|
||||
return Tree(std::in_place_index<TokenIdx::Nil>);
|
||||
} else if (sym.size() == 1 && sym == "?") {
|
||||
return Tree(std::in_place_index<TokenIdx::Chk>);
|
||||
} else if (auto const &o = oprs.find(sym[0]); sym.size() == 1 && o != oprs.end()) {
|
||||
return Tree(std::in_place_index<TokenIdx::Opr>, *o);
|
||||
} else if (auto const &c = cmps.find(sym[0]); sym.size() == 1 && c != cmps.end()) {
|
||||
return Tree(std::in_place_index<TokenIdx::Cmp>, *c);
|
||||
} else try {
|
||||
return Tree(std::in_place_index<TokenIdx::Int>, BigInt::from_string(sym));
|
||||
} catch (...) {
|
||||
throw std::runtime_error("invalid symbol: " + std::string(sym));
|
||||
}
|
||||
}
|
||||
void calc() {
|
||||
static const auto T = Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("T", Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("F", Tree(std::in_place_index<TokenIdx::Par>, "T")))));
|
||||
static const auto F = Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("T", Tree(std::in_place_index<TokenIdx::LEF>, Box<std::pair<std::string, Tree>>::make("F", Tree(std::in_place_index<TokenIdx::Par>, "F")))));
|
||||
if (chk_stack()) {
|
||||
throw std::runtime_error("recursion limit exceeded");
|
||||
}
|
||||
tail_call:
|
||||
if (chk_flag()) {
|
||||
throw std::runtime_error("keyboard interrupt");
|
||||
}
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
fst.calc();
|
||||
if (auto pnil = std::get_if<TokenIdx::Nil>(&fst.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else if (auto pchk = std::get_if<TokenIdx::Chk>(&fst.token)) {
|
||||
snd.calc();
|
||||
*this = snd.token.index() == TokenIdx::Nil ? F : T;
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&fst.token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
tmp.substitute(std::make_shared<std::pair<Tree, bool>>(std::move(snd), 0), std::move(par));
|
||||
*this = Tree(std::move(tmp));
|
||||
goto tail_call;
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&fst.token)) {
|
||||
snd.calc();
|
||||
auto &[par, tmp] = **peef;
|
||||
tmp.substitute(std::make_shared<std::pair<Tree, bool>>(std::move(snd), 1), std::move(par));
|
||||
*this = Tree(std::move(tmp));
|
||||
goto tail_call;
|
||||
} else if (auto popr = std::get_if<TokenIdx::Opr>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token); pint && (*pint || popr->first != '/' && popr->first != '%')) {
|
||||
token = std::make_pair(std::move(*popr), std::move(*pint));
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto pcmp = std::get_if<TokenIdx::Cmp>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token)) {
|
||||
token = std::make_pair(std::move(*pcmp), std::move(*pint));
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto paoi = std::get_if<TokenIdx::AOI>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token)) {
|
||||
token = paoi->first.second(std::move(*pint), std::move(paoi->second));
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto paci = std::get_if<TokenIdx::ACI>(&fst.token)) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(&snd.token)) {
|
||||
*this = paci->first.second(std::move(*pint), std::move(paci->second)) ? T : F;
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(&snd.token)) {
|
||||
token.emplace<TokenIdx::Nil>();
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("invalid function: " + fst.translate());
|
||||
}
|
||||
} else if (auto parg = std::get_if<TokenIdx::Arg>(&token)) {
|
||||
auto &shr = (*parg)->first;
|
||||
auto rec = (*parg)->second;
|
||||
if (parg->use_count() == 1) {
|
||||
*this = Tree(std::move(shr));
|
||||
if (not rec) {
|
||||
goto tail_call;
|
||||
}
|
||||
} else {
|
||||
if (not rec) {
|
||||
shr.calc();
|
||||
rec = true;
|
||||
}
|
||||
*this = shr;
|
||||
}
|
||||
} else if (auto pglb = std::get_if<TokenIdx::Glb>(&token)) {
|
||||
if (auto const &itr = map.find(*pglb); itr != map.end()) {
|
||||
*this = itr->second;
|
||||
goto tail_call;
|
||||
} else {
|
||||
throw std::runtime_error("undefined symbol: &" + *pglb);
|
||||
}
|
||||
}
|
||||
}
|
||||
void substitute(std::shared_ptr<std::pair<Tree, bool>> const &arg, std::string const &tar) {
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
fst.substitute(arg, tar);
|
||||
snd.substitute(arg, tar);
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
if (par != tar) {
|
||||
tmp.substitute(arg, tar);
|
||||
}
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **peef;
|
||||
if (par != tar) {
|
||||
tmp.substitute(arg, tar);
|
||||
}
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(&token)) {
|
||||
if (*ppar == tar) {
|
||||
token.emplace<TokenIdx::Arg>(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
void analyze(std::unordered_set<std::string> &set) const {
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
fst.analyze(set);
|
||||
snd.analyze(set);
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
if (set.find(par) != set.end()) {
|
||||
tmp.analyze(set);
|
||||
} else {
|
||||
auto pos = set.insert(par).first;
|
||||
tmp.analyze(set);
|
||||
set.erase(pos);
|
||||
}
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **peef;
|
||||
if (set.find(par) != set.end()) {
|
||||
tmp.analyze(set);
|
||||
} else {
|
||||
auto pos = set.insert(par).first;
|
||||
tmp.analyze(set);
|
||||
set.erase(pos);
|
||||
}
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(&token)) {
|
||||
if (set.find(*ppar) == set.end()) {
|
||||
throw std::runtime_error("unbound variable: $" + *ppar);
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline std::unordered_map<std::string, Tree> map;
|
||||
public:
|
||||
Tree(Tree const &other): token(other.token) {}
|
||||
Tree &operator=(Tree const &other) {
|
||||
token = other.token;
|
||||
return *this;
|
||||
}
|
||||
Tree(Tree &&other): token(std::exchange(other.token, std::nullopt)) {}
|
||||
Tree &operator=(Tree &&other) {
|
||||
token = std::exchange(other.token, std::nullopt);
|
||||
return *this;
|
||||
}
|
||||
~Tree() {
|
||||
if (token.index() == TokenIdx::Und) {
|
||||
return;
|
||||
}
|
||||
std::queue<TokenVar> flat;
|
||||
flat.push(std::exchange(token, std::nullopt));
|
||||
for (; not flat.empty(); flat.pop()) {
|
||||
auto &token = flat.front();
|
||||
if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
flat.push(std::exchange(fst.token, std::nullopt));
|
||||
flat.push(std::exchange(snd.token, std::nullopt));
|
||||
} else if (auto parg = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **parg;
|
||||
flat.push(std::exchange(tmp.token, std::nullopt));
|
||||
} else if (auto parg = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **parg;
|
||||
flat.push(std::exchange(tmp.token, std::nullopt));
|
||||
} else if (auto parg = std::get_if<TokenIdx::Arg>(&token)) {
|
||||
auto &shr = (*parg)->first;
|
||||
if (parg->use_count() == 1) {
|
||||
flat.push(std::exchange(shr.token, std::nullopt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static auto cal(Slice &&exp) {
|
||||
auto res = parse(std::move(exp));
|
||||
std::unordered_set<std::string> set;
|
||||
res.analyze(set);
|
||||
clr_flag();
|
||||
res.calc();
|
||||
return res;
|
||||
}
|
||||
static void def(Slice &&exp, std::string const &glb) {
|
||||
std::unordered_set<std::string> set;
|
||||
auto res = parse(std::move(exp));
|
||||
res.analyze(set);
|
||||
map.insert_or_assign(glb, std::move(res));
|
||||
}
|
||||
static auto const &dir() {
|
||||
return map;
|
||||
}
|
||||
static void clr() {
|
||||
return map.clear();
|
||||
}
|
||||
std::string translate(bool lb = 0, bool rb = 0) const {
|
||||
if (auto pnil = std::get_if<TokenIdx::Nil>(&token)) {
|
||||
return "...";
|
||||
} else if (auto pchk = std::get_if<TokenIdx::Chk>(&token)) {
|
||||
return "?";
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(&token)) {
|
||||
auto &[par, tmp] = **plef;
|
||||
auto s = "\\" + par + " " + tmp.translate(0, rb && !rb);
|
||||
return rb ? "(" + s + ")" : s;
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(&token)) {
|
||||
auto &[par, tmp] = **peef;
|
||||
auto s = "^" + par + " " + tmp.translate(0, rb && !rb);
|
||||
return rb ? "(" + s + ")" : s;
|
||||
} else if (auto pint = std::get_if<TokenIdx::Int>(&token)) {
|
||||
return pint->to_string();
|
||||
} else if (auto popr = std::get_if<TokenIdx::Opr>(&token)) {
|
||||
return std::string{popr->first};
|
||||
} else if (auto pcmp = std::get_if<TokenIdx::Cmp>(&token)) {
|
||||
return std::string{pcmp->first};
|
||||
} else if (auto paoi = std::get_if<TokenIdx::AOI>(&token)) {
|
||||
auto s = std::string{paoi->first.first, ' '} + paoi->second.to_string();
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto paci = std::get_if<TokenIdx::ACI>(&token)) {
|
||||
auto s = std::string{paci->first.first, ' '} + paci->second.to_string();
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto papp = std::get_if<TokenIdx::App>(&token)) {
|
||||
auto &[fst, snd] = **papp;
|
||||
auto s = fst.translate(lb && !lb, 1) + " " + snd.translate(1, rb && !lb);
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto parg = std::get_if<TokenIdx::Arg>(&token)) {
|
||||
return (*parg)->first.translate(lb, rb);
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(&token)) {
|
||||
return "$" + *ppar;
|
||||
} else if (auto pglb = std::get_if<TokenIdx::Glb>(&token)) {
|
||||
return "&" + *pglb;
|
||||
} else {
|
||||
assert(false); // unreachable
|
||||
}
|
||||
}
|
||||
};
|
||||
int main(int argc, char *argv[]) {
|
||||
ini_stack();
|
||||
bool check_stdin = false;
|
||||
bool check_stdout = false;
|
||||
bool check_stderr = false;
|
||||
#if defined _WIN32
|
||||
DWORD dwModeTemp;
|
||||
check_stdin = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwModeTemp);
|
||||
check_stdout = GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwModeTemp);
|
||||
check_stderr = GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &dwModeTemp);
|
||||
#elif defined __unix__
|
||||
check_stdin = isatty(fileno(stdin));
|
||||
check_stdout = isatty(fileno(stdout));
|
||||
check_stderr = isatty(fileno(stderr));
|
||||
struct rlimit rlim;
|
||||
getrlimit(RLIMIT_STACK, &rlim);
|
||||
rlim.rlim_cur = STACK_SIZE;
|
||||
setrlimit(RLIMIT_STACK, &rlim);
|
||||
struct sigaction act;
|
||||
act.sa_handler = set_flag;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0; // use SA_RESTART to avoid getting EOF when SIGINT is received during input
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
#endif
|
||||
std::string ps_in = check_stderr && check_stdin ? ">> " : "";
|
||||
std::string ps_out = check_stderr && check_stdout ? "=> " : "";
|
||||
std::string ps_res = check_stderr && check_stdout ? "== " : "";
|
||||
for (bool end = false; not end;) {
|
||||
std::cerr << ps_in;
|
||||
Slice exp = Slice::getline(std::cin);
|
||||
if (std::cin.eof()) {
|
||||
end = true;
|
||||
if (check_stderr && check_stdin) {
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (auto cmd = read(exp); cmd.empty() || cmd.size() == 1 && cmd == "#") {
|
||||
continue;
|
||||
} else if (cmd[0] == ':') {
|
||||
Tree::def(std::move(exp), cmd(1, 0));
|
||||
} else if (cmd.size() == 3 && cmd == "cal") {
|
||||
auto res = Tree::cal(std::move(exp));
|
||||
std::cerr << ps_res;
|
||||
std::cout << res.translate() << std::endl;
|
||||
} else if (cmd.size() == 3 && cmd == "dir") {
|
||||
for (auto &[glb, tmp] : Tree::dir()) {
|
||||
std::cerr << ps_out;
|
||||
std::cout << std::left << std::setw(10) << ":" + (glb.size() <= 8 ? glb : glb.substr(0, 6) + "..") << tmp.translate() << std::endl;
|
||||
}
|
||||
} else if (cmd.size() == 3 && cmd == "clr") {
|
||||
Tree::clr();
|
||||
} else if (cmd.size() == 3 && cmd == "end") {
|
||||
end = true;
|
||||
} else {
|
||||
throw std::runtime_error("unknown command: " + std::string(cmd));
|
||||
}
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << "Runtime Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,453 @@
|
|||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "slice.hpp"
|
||||
#ifndef USE_GMP
|
||||
#include "bigint_nat.hpp" // native big integer
|
||||
#else
|
||||
#include "bigint_gmp.hpp" // GNU MP big integer
|
||||
#endif
|
||||
#if defined _WIN32
|
||||
#include <Windows.h>
|
||||
#elif defined __unix__
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#ifndef STACK_SIZE
|
||||
#define STACK_SIZE 8388608 // 8 MiB
|
||||
#endif
|
||||
char *stack_top;
|
||||
char *stack_cur;
|
||||
void ini_stack() {
|
||||
char dummy;
|
||||
stack_top = &dummy;
|
||||
}
|
||||
bool chk_stack() {
|
||||
char dummy;
|
||||
stack_cur = &dummy;
|
||||
return stack_top - stack_cur >= STACK_SIZE / 2;
|
||||
}
|
||||
#if defined _WIN32
|
||||
void clr_flag() {
|
||||
GetAsyncKeyState(VK_ESCAPE);
|
||||
}
|
||||
bool chk_flag() {
|
||||
return GetAsyncKeyState(VK_ESCAPE) & 0x0001;
|
||||
}
|
||||
#elif defined __unix__
|
||||
bool flag_rec;
|
||||
void set_flag(int) {
|
||||
flag_rec = 1;
|
||||
}
|
||||
void clr_flag() {
|
||||
flag_rec = 0;
|
||||
}
|
||||
bool chk_flag() {
|
||||
return flag_rec;
|
||||
}
|
||||
#endif
|
||||
auto read(Slice &exp) {
|
||||
auto i = exp.get_beg();
|
||||
auto n = exp.get_end();
|
||||
for (;; i++) {
|
||||
if (i == n) {
|
||||
return exp.reset_to(i, n), exp.from_to(i, i);
|
||||
} else if (*i != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto j = i;
|
||||
auto c = 0;
|
||||
for (;; i++) {
|
||||
if ((i == n || *i == ' ') && c == 0) {
|
||||
return exp.reset_to(i, n), exp.from_to(j, i);
|
||||
} else if (i == n) {
|
||||
throw std::runtime_error("mismatched parentheses");
|
||||
} else if (*i == '(') {
|
||||
c++;
|
||||
} else if (*i == ')') {
|
||||
c--;
|
||||
}
|
||||
}
|
||||
}
|
||||
BigInt operator+(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator-(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator*(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator/(BigInt const &lval, BigInt const &rval);
|
||||
BigInt operator%(BigInt const &lval, BigInt const &rval);
|
||||
bool operator>(BigInt const &lval, BigInt const &rval);
|
||||
bool operator<(BigInt const &lval, BigInt const &rval);
|
||||
bool operator>=(BigInt const &lval, BigInt const &rval);
|
||||
bool operator<=(BigInt const &lval, BigInt const &rval);
|
||||
bool operator==(BigInt const &lval, BigInt const &rval);
|
||||
bool operator!=(BigInt const &lval, BigInt const &rval);
|
||||
typedef BigInt (*opr_t)(BigInt const &, BigInt const &);
|
||||
typedef bool (*cmp_t)(BigInt const &, BigInt const &);
|
||||
static inline std::unordered_map<char, opr_t> const oprs = {
|
||||
{'+', operator+},
|
||||
{'-', operator-},
|
||||
{'*', operator*},
|
||||
{'/', operator/},
|
||||
{'%', operator%},
|
||||
};
|
||||
static inline std::unordered_map<char, cmp_t> const cmps = {
|
||||
{'>', operator>},
|
||||
{'<', operator<},
|
||||
{'=', operator==},
|
||||
};
|
||||
class Tree {
|
||||
enum TokenIdx: std::size_t {
|
||||
Nil, Chk,
|
||||
Par, Int,
|
||||
Opr, AOI,
|
||||
Cmp, ACI,
|
||||
LEF, EEF, // Lazy/Eager-Evaluation Function
|
||||
App,
|
||||
};
|
||||
using TokenVar = std::variant<
|
||||
std::monostate, std::monostate, std::string, BigInt,
|
||||
std::pair<char, opr_t>, std::pair<std::pair<char, opr_t>, BigInt>,
|
||||
std::pair<char, cmp_t>, std::pair<std::pair<char, cmp_t>, BigInt>,
|
||||
std::pair<std::string, Tree>, std::pair<std::string, Tree>,
|
||||
std::pair<Tree, Tree>>;
|
||||
std::shared_ptr<TokenVar> sp; bool lb;
|
||||
Tree(TokenVar *ptr): sp(ptr), lb(0) {}
|
||||
static Tree first(Tree &&fst) {
|
||||
if (fst.sp == nullptr) {
|
||||
throw std::runtime_error("empty expression");
|
||||
} else {
|
||||
return std::move(fst);
|
||||
}
|
||||
}
|
||||
static Tree build(Tree &&fst, Tree &&snd) {
|
||||
if (fst.sp == nullptr) {
|
||||
return std::move(snd);
|
||||
} else {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::App>, std::move(fst), std::move(snd));
|
||||
}
|
||||
}
|
||||
static Tree parse(Slice &&exp, Tree &&fun = nullptr, Tree &&fst = nullptr) {
|
||||
if (auto sym = read(exp); sym.empty()) {
|
||||
return build(std::move(fun), first(std::move(fst)));
|
||||
} else if (sym[0] == '\\') {
|
||||
return build(std::move(fun), build(std::move(fst), new TokenVar(std::in_place_index<TokenIdx::LEF>, sym(1, 0), parse(std::move(exp)))));
|
||||
} else if (sym[0] == '|') {
|
||||
return parse(std::move(exp), new TokenVar(std::in_place_index<TokenIdx::LEF>, sym(1, 0), build(std::move(fun), first(std::move(fst)))));
|
||||
} else if (sym[0] == '^') {
|
||||
return build(std::move(fun), build(std::move(fst), new TokenVar(std::in_place_index<TokenIdx::EEF>, sym(1, 0), parse(std::move(exp)))));
|
||||
} else if (sym[0] == '@') {
|
||||
return parse(std::move(exp), new TokenVar(std::in_place_index<TokenIdx::EEF>, sym(1, 0), build(std::move(fun), first(std::move(fst)))));
|
||||
} else {
|
||||
return parse(std::move(exp), std::move(fun), build(std::move(fst), lex(std::move(sym))));
|
||||
}
|
||||
}
|
||||
static Tree lex(Slice const &sym) {
|
||||
if (sym[0] == '(' && sym[-1] == ')') {
|
||||
return parse(sym(1, -1));
|
||||
} else if (sym[0] == '$') {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::Par>, sym(1, 0));
|
||||
} else if (sym.size() == 3 && sym == "...") {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::Nil>);
|
||||
} else if (sym.size() == 1 && sym == "?") {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::Chk>);
|
||||
} else if (auto const &o = oprs.find(sym[0]); sym.size() == 1 && o != oprs.end()) {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::Opr>, *o);
|
||||
} else if (auto const &c = cmps.find(sym[0]); sym.size() == 1 && c != cmps.end()) {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::Cmp>, *c);
|
||||
} else try {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::Int>, BigInt::from_string(sym));
|
||||
} catch (...) {
|
||||
throw std::runtime_error("invalid symbol: " + std::string(sym));
|
||||
}
|
||||
}
|
||||
void calc() {
|
||||
static const auto T = Tree(new TokenVar(std::in_place_index<TokenIdx::LEF>, "T", Tree(new TokenVar(std::in_place_index<TokenIdx::LEF>, "F", Tree(new TokenVar(std::in_place_index<TokenIdx::Par>, "T"))))));
|
||||
static const auto F = Tree(new TokenVar(std::in_place_index<TokenIdx::LEF>, "T", Tree(new TokenVar(std::in_place_index<TokenIdx::LEF>, "F", Tree(new TokenVar(std::in_place_index<TokenIdx::Par>, "F"))))));
|
||||
static const auto N = Tree(new TokenVar(std::in_place_index<TokenIdx::Nil>));
|
||||
if (chk_stack()) {
|
||||
throw std::runtime_error("recursion limit exceeded");
|
||||
}
|
||||
if (chk_flag()) {
|
||||
throw std::runtime_error("keyboard interrupt");
|
||||
}
|
||||
if (auto papp = std::get_if<TokenIdx::App>(sp.get())) {
|
||||
auto &[fst, snd] = *papp;
|
||||
fst.calc();
|
||||
snd.lb = 1;
|
||||
if (auto pnil = std::get_if<TokenIdx::Nil>(fst.sp.get())) {
|
||||
*sp = *N.sp;
|
||||
} else if (auto pchk = std::get_if<TokenIdx::Chk>(fst.sp.get())) {
|
||||
snd.calc();
|
||||
*sp = snd.sp->index() == TokenIdx::Nil ? *F.sp : *T.sp;
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(fst.sp.get())) {
|
||||
auto &[par, tmp] = *plef;
|
||||
auto tsb = tmp.substitute(snd, par);
|
||||
tsb.calc();
|
||||
*sp = *tsb.sp;
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(fst.sp.get())) {
|
||||
snd.calc();
|
||||
auto &[par, tmp] = *peef;
|
||||
auto tsb = tmp.substitute(snd, par);
|
||||
tsb.calc();
|
||||
*sp = *tsb.sp;
|
||||
} else if (auto popr = std::get_if<TokenIdx::Opr>(fst.sp.get())) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(snd.sp.get()); pint && (*pint || popr->first != '/' && popr->first != '%')) {
|
||||
*sp = std::make_pair(*popr, *pint);
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(snd.sp.get())) {
|
||||
*sp = *N.sp;
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto pcmp = std::get_if<TokenIdx::Cmp>(fst.sp.get())) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(snd.sp.get())) {
|
||||
*sp = std::make_pair(*pcmp, *pint);
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(snd.sp.get())) {
|
||||
*sp = *N.sp;
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto paoi = std::get_if<TokenIdx::AOI>(fst.sp.get())) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(snd.sp.get())) {
|
||||
*sp = paoi->first.second(*pint, paoi->second);
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(snd.sp.get())) {
|
||||
*sp = *N.sp;
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else if (auto paci = std::get_if<TokenIdx::ACI>(fst.sp.get())) {
|
||||
snd.calc();
|
||||
if (auto pint = std::get_if<TokenIdx::Int>(snd.sp.get())) {
|
||||
*sp = paci->first.second(*pint, paci->second) ? *T.sp : *F.sp;
|
||||
} else if (auto pnil = std::get_if<TokenIdx::Nil>(snd.sp.get())) {
|
||||
*sp = *N.sp;
|
||||
} else {
|
||||
throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate());
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("invalid function: " + fst.translate());
|
||||
}
|
||||
}
|
||||
}
|
||||
Tree substitute(Tree const &arg, std::string const &tar) {
|
||||
if (lb) {
|
||||
return *this;
|
||||
}
|
||||
if (auto papp = std::get_if<TokenIdx::App>(sp.get())) {
|
||||
auto &[fst, snd] = *papp;
|
||||
auto fsb = fst.substitute(arg, tar);
|
||||
auto ssb = snd.substitute(arg, tar);
|
||||
if (fsb.sp != fst.sp || ssb.sp != snd.sp) {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::App>, std::move(fsb), std::move(ssb));
|
||||
}
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(sp.get())) {
|
||||
auto &[par, tmp] = *plef;
|
||||
if (par != tar) {
|
||||
auto tsb = tmp.substitute(arg, tar);
|
||||
if (tsb.sp != tmp.sp) {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::LEF>, par, std::move(tsb));
|
||||
}
|
||||
}
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(sp.get())) {
|
||||
auto &[par, tmp] = *peef;
|
||||
if (par != tar) {
|
||||
auto tsb = tmp.substitute(arg, tar);
|
||||
if (tsb.sp != tmp.sp) {
|
||||
return new TokenVar(std::in_place_index<TokenIdx::EEF>, par, std::move(tsb));
|
||||
}
|
||||
}
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(sp.get())) {
|
||||
if (*ppar == tar) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void analyze(std::unordered_set<std::string> &set) {
|
||||
if (auto papp = std::get_if<TokenIdx::App>(sp.get())) {
|
||||
auto &[fst, snd] = *papp;
|
||||
fst.analyze(set);
|
||||
snd.analyze(set);
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(sp.get())) {
|
||||
auto &[par, tmp] = *plef;
|
||||
if (set.find(par) != set.end()) {
|
||||
tmp.analyze(set);
|
||||
} else {
|
||||
auto pos = set.insert(par).first;
|
||||
tmp.analyze(set);
|
||||
set.erase(pos);
|
||||
}
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(sp.get())) {
|
||||
auto &[par, tmp] = *peef;
|
||||
if (set.find(par) != set.end()) {
|
||||
tmp.analyze(set);
|
||||
} else {
|
||||
auto pos = set.insert(par).first;
|
||||
tmp.analyze(set);
|
||||
set.erase(pos);
|
||||
}
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(sp.get())) {
|
||||
if (set.find(*ppar) == set.end()) {
|
||||
if (auto const &itr = map.find(*ppar); itr != map.end()) {
|
||||
*this = itr->second;
|
||||
} else {
|
||||
throw std::runtime_error("unbound variable: $" + *ppar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline std::unordered_map<std::string, Tree> map;
|
||||
public:
|
||||
Tree(Tree const &other) = default;
|
||||
Tree &operator=(Tree const &) = default;
|
||||
Tree(Tree &&) = default;
|
||||
Tree &operator=(Tree &&) = default;
|
||||
~Tree() {
|
||||
if (sp == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::queue<std::shared_ptr<TokenVar>> flat;
|
||||
flat.push(std::move(sp));
|
||||
for (; not flat.empty(); flat.pop()) {
|
||||
if (auto &sp = flat.front(); sp.use_count() == 1) {
|
||||
if (auto papp = std::get_if<TokenIdx::App>(sp.get())) {
|
||||
auto &[fst, snd] = *papp;
|
||||
flat.push(std::move(fst.sp));
|
||||
flat.push(std::move(snd.sp));
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(sp.get())) {
|
||||
auto &[par, tmp] = *plef;
|
||||
flat.push(std::move(tmp.sp));
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(sp.get())) {
|
||||
auto &[par, tmp] = *peef;
|
||||
flat.push(std::move(tmp.sp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static auto const &put(Slice &&exp, std::string const &par, bool calc) {
|
||||
auto res = parse(std::move(exp));
|
||||
std::unordered_set<std::string> set;
|
||||
res.analyze(set);
|
||||
map.erase(par);
|
||||
if (calc) {
|
||||
clr_flag();
|
||||
res.calc();
|
||||
}
|
||||
return map.emplace(par, res).first->second;
|
||||
}
|
||||
static auto const &dir() {
|
||||
return map;
|
||||
}
|
||||
static void clr() {
|
||||
return map.clear();
|
||||
}
|
||||
std::string translate(bool lb = 0, bool rb = 0) const {
|
||||
if (auto pnil = std::get_if<TokenIdx::Nil>(sp.get())) {
|
||||
return "...";
|
||||
} else if (auto pchk = std::get_if<TokenIdx::Chk>(sp.get())) {
|
||||
return "?";
|
||||
} else if (auto plef = std::get_if<TokenIdx::LEF>(sp.get())) {
|
||||
auto &[par, tmp] = *plef;
|
||||
auto s = "\\" + par + " " + tmp.translate(0, rb && !rb);
|
||||
return rb ? "(" + s + ")" : s;
|
||||
} else if (auto peef = std::get_if<TokenIdx::EEF>(sp.get())) {
|
||||
auto &[par, tmp] = *peef;
|
||||
auto s = "^" + par + " " + tmp.translate(0, rb && !rb);
|
||||
return rb ? "(" + s + ")" : s;
|
||||
} else if (auto pint = std::get_if<TokenIdx::Int>(sp.get())) {
|
||||
return pint->to_string();
|
||||
} else if (auto popr = std::get_if<TokenIdx::Opr>(sp.get())) {
|
||||
return std::string{popr->first};
|
||||
} else if (auto pcmp = std::get_if<TokenIdx::Cmp>(sp.get())) {
|
||||
return std::string{pcmp->first};
|
||||
} else if (auto paoi = std::get_if<TokenIdx::AOI>(sp.get())) {
|
||||
auto s = std::string{paoi->first.first, ' '} + paoi->second.to_string();
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto paci = std::get_if<TokenIdx::ACI>(sp.get())) {
|
||||
auto s = std::string{paci->first.first, ' '} + paci->second.to_string();
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto papp = std::get_if<TokenIdx::App>(sp.get())) {
|
||||
auto &[fst, snd] = *papp;
|
||||
auto s = fst.translate(lb && !lb, 1) + " " + snd.translate(1, rb && !lb);
|
||||
return lb ? "(" + s + ")" : s;
|
||||
} else if (auto ppar = std::get_if<TokenIdx::Par>(sp.get())) {
|
||||
return "$" + *ppar;
|
||||
} else {
|
||||
assert(false); // unreachable
|
||||
}
|
||||
}
|
||||
};
|
||||
int main(int argc, char *argv[]) {
|
||||
ini_stack();
|
||||
bool check_stdin = false;
|
||||
bool check_stdout = false;
|
||||
bool check_stderr = false;
|
||||
#if defined _WIN32
|
||||
DWORD dwModeTemp;
|
||||
check_stdin = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwModeTemp);
|
||||
check_stdout = GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwModeTemp);
|
||||
check_stderr = GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &dwModeTemp);
|
||||
#elif defined __unix__
|
||||
check_stdin = isatty(fileno(stdin));
|
||||
check_stdout = isatty(fileno(stdout));
|
||||
check_stderr = isatty(fileno(stderr));
|
||||
struct rlimit rlim;
|
||||
getrlimit(RLIMIT_STACK, &rlim);
|
||||
rlim.rlim_cur = STACK_SIZE;
|
||||
setrlimit(RLIMIT_STACK, &rlim);
|
||||
struct sigaction act;
|
||||
act.sa_handler = set_flag;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0; // use SA_RESTART to avoid getting EOF when SIGINT is received during input
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
#endif
|
||||
std::string ps_in = check_stderr && check_stdin ? ">> " : "";
|
||||
std::string ps_out = check_stderr && check_stdout ? "=> " : "";
|
||||
std::string ps_res = check_stderr && check_stdout ? "== " : "";
|
||||
for (bool end = false; not end;) {
|
||||
std::cerr << ps_in;
|
||||
Slice exp = Slice::getline(std::cin);
|
||||
if (std::cin.eof()) {
|
||||
end = true;
|
||||
if (check_stderr && check_stdin) {
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (auto cmd = read(exp); cmd.empty() || cmd.size() == 1 && cmd == "#") {
|
||||
continue;
|
||||
} else if (cmd[0] == ':') {
|
||||
Tree::put(std::move(exp), cmd(1, 0), 0);
|
||||
} else if (cmd.size() == 3 && cmd == "cal") {
|
||||
auto &res = Tree::put(std::move(exp), "", 1);
|
||||
std::cerr << ps_res;
|
||||
std::cout << res.translate() << std::endl;
|
||||
} else if (cmd.size() == 3 && cmd == "dir") {
|
||||
for (auto &[par, arg] : Tree::dir()) {
|
||||
std::cerr << ps_out;
|
||||
std::cout << ":" + par << std::endl;
|
||||
}
|
||||
} else if (cmd.size() == 3 && cmd == "clr") {
|
||||
Tree::clr();
|
||||
} else if (cmd.size() == 3 && cmd == "end") {
|
||||
end = true;
|
||||
} else {
|
||||
throw std::runtime_error("unknown command: " + std::string(cmd));
|
||||
}
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << "Runtime Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
class Slice {
|
||||
std::shared_ptr<char const []> arr;
|
||||
char const *beg;
|
||||
char const *end;
|
||||
Slice(std::shared_ptr<char const []> const &arr, char const *beg, char const *end): arr(arr), beg(beg), end(end) {}
|
||||
public:
|
||||
static Slice getline(std::istream &is) {
|
||||
std::size_t cap = 16;
|
||||
std::size_t len = 0;
|
||||
auto arr = std::make_shared<char []>(cap);
|
||||
char *beg = &arr[0x0];
|
||||
char *end = &arr[len];
|
||||
for (char c; is.get(c) && c != '\n'; len++, *end++ = c) {
|
||||
if (len == cap) {
|
||||
cap *= 2;
|
||||
auto tmp = std::make_shared<char []>(cap);
|
||||
std::copy(beg, end, tmp.get());
|
||||
arr = tmp;
|
||||
beg = &arr[0x0];
|
||||
end = &arr[len];
|
||||
}
|
||||
}
|
||||
return Slice(arr, beg, end);
|
||||
}
|
||||
operator std::string() const {
|
||||
return std::string(beg, end);
|
||||
}
|
||||
operator std::string_view() const {
|
||||
return std::string_view(beg, end - beg);
|
||||
}
|
||||
bool operator==(char const *str) const {
|
||||
return std::equal(beg, end, str);
|
||||
}
|
||||
bool empty() const {
|
||||
return beg == end;
|
||||
}
|
||||
size_t size() const {
|
||||
return end - beg;
|
||||
}
|
||||
char operator[](std::ptrdiff_t idx) const {
|
||||
return (idx < 0 ? end : beg)[idx];
|
||||
}
|
||||
Slice operator()(std::ptrdiff_t add, std::ptrdiff_t sub) const {
|
||||
return Slice(arr, (add < 0 ? end : beg) + add, (sub > 0 ? beg : end) + sub);
|
||||
}
|
||||
char const *get_beg() const {
|
||||
return beg;
|
||||
}
|
||||
char const *get_end() const {
|
||||
return end;
|
||||
}
|
||||
Slice from_to(char const *beg, char const *end) const {
|
||||
return Slice(arr, beg, end);
|
||||
}
|
||||
void reset_to(char const *beg, char const *end) {
|
||||
this->beg = beg;
|
||||
this->end = end;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue