From ba458fffba16f4def35f30d8fbec20e149e9241f Mon Sep 17 00:00:00 2001 From: T zkdream Date: Tue, 9 Apr 2024 13:40:20 +0300 Subject: [PATCH] boom --- .clang-format | 28 +++ .gitignore | 1 + Makefile | 38 ++++ bigint_gmp.hpp | 53 +++++ bigint_nat.hpp | 232 ++++++++++++++++++++ build.sh | 5 + container.hpp | 105 +++++++++ examples/bf2instrs.py | 50 +++++ examples/bf_cbn.λ | 20 ++ examples/bf_cbv.λ | 20 ++ examples/test_cbn.λ | 54 +++++ examples/test_cbv.λ | 54 +++++ lambda_cbn.cpp | 471 +++++++++++++++++++++++++++++++++++++++++ lambda_cbv.cpp | 481 ++++++++++++++++++++++++++++++++++++++++++ lambda_exp.cpp | 453 +++++++++++++++++++++++++++++++++++++++ slice.hpp | 63 ++++++ 16 files changed, 2128 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 bigint_gmp.hpp create mode 100644 bigint_nat.hpp create mode 100755 build.sh create mode 100644 container.hpp create mode 100644 examples/bf2instrs.py create mode 100644 examples/bf_cbn.λ create mode 100644 examples/bf_cbv.λ create mode 100644 examples/test_cbn.λ create mode 100644 examples/test_cbv.λ create mode 100644 lambda_cbn.cpp create mode 100644 lambda_cbv.cpp create mode 100644 lambda_exp.cpp create mode 100644 slice.hpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5c7ce7d --- /dev/null +++ b/.clang-format @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..33ed67e --- /dev/null +++ b/Makefile @@ -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) diff --git a/bigint_gmp.hpp b/bigint_gmp.hpp new file mode 100644 index 0000000..8a647a0 --- /dev/null +++ b/bigint_gmp.hpp @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +class BigInt { + mpz_class data; + template + BigInt(T &&...args): + data(std::forward(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; + } +}; diff --git a/bigint_nat.hpp b/bigint_nat.hpp new file mode 100644 index 0000000..fad7984 --- /dev/null +++ b/bigint_nat.hpp @@ -0,0 +1,232 @@ +#pragma once +#include +#include +#include +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 + 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 + 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(lbi, rbi); + } + friend bool operator<(BigInt const &lbi, BigInt const &rbi) { + return compare(lbi, rbi); + } + friend bool operator>=(BigInt const &lbi, BigInt const &rbi) { + return compare(lbi, rbi); + } + friend bool operator<=(BigInt const &lbi, BigInt const &rbi) { + return compare(lbi, rbi); + } + friend bool operator==(BigInt const &lbi, BigInt const &rbi) { + return compare(lbi, rbi); + } + friend bool operator!=(BigInt const &lbi, BigInt const &rbi) { + return compare(lbi, rbi); + } +}; diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b876b2b --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +for f in *.cpp; do + make SOURCE=$f + make SOURCE=$f USE_GMP=1 +done diff --git a/container.hpp b/container.hpp new file mode 100644 index 0000000..06eaaf9 --- /dev/null +++ b/container.hpp @@ -0,0 +1,105 @@ +#pragma once +#include +#include +// 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 +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 + static Opt make(Args &&...args) { + return Opt(new T(std::forward(args)...)); + } + T &operator*() const & { + return *data; + } + T *operator->() const & { + return data; + } + operator bool() const { + return data; + } +}; +template +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 + static Box make(Args &&...args) { + return Box(new T(std::forward(args)...)); + } + T const &operator*() const & { + return *data; + } + T const *operator->() const & { + return data; + } + T &operator*() & { + return *data; + } + T *operator->() & { + return data; + } +}; diff --git a/examples/bf2instrs.py b/examples/bf2instrs.py new file mode 100644 index 0000000..3c0fa97 --- /dev/null +++ b/examples/bf2instrs.py @@ -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() diff --git a/examples/bf_cbn.λ b/examples/bf_cbn.λ new file mode 100644 index 0000000..2ed0199 --- /dev/null +++ b/examples/bf_cbn.λ @@ -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 diff --git a/examples/bf_cbv.λ b/examples/bf_cbv.λ new file mode 100644 index 0000000..3d86c54 --- /dev/null +++ b/examples/bf_cbv.λ @@ -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 diff --git a/examples/test_cbn.λ b/examples/test_cbn.λ new file mode 100644 index 0000000..a490bb3 --- /dev/null +++ b/examples/test_cbn.λ @@ -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 diff --git a/examples/test_cbv.λ b/examples/test_cbv.λ new file mode 100644 index 0000000..fb4014d --- /dev/null +++ b/examples/test_cbv.λ @@ -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 diff --git a/lambda_cbn.cpp b/lambda_cbn.cpp new file mode 100644 index 0000000..d088b03 --- /dev/null +++ b/lambda_cbn.cpp @@ -0,0 +1,471 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#elif defined __unix__ +#include +#include +#include +#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 const oprs = { + {'+', operator+}, + {'-', operator-}, + {'*', operator*}, + {'/', operator/}, + {'%', operator%}, +}; +static inline std::unordered_map 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, std::pair, BigInt>, + std::pair, std::pair, BigInt>, + Box>, Box>, + Box>, std::shared_ptr>>; + TokenVar token; + template >> + Tree(Args &&...args): token(std::forward(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>::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, Box>::make(sym(1, 0), parse(std::move(exp)))))); + } else if (sym[0] == '|') { + return parse(std::move(exp), Tree(std::in_place_index, Box>::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, Box>::make(sym(1, 0), parse(std::move(exp)))))); + } else if (sym[0] == '@') { + return parse(std::move(exp), Tree(std::in_place_index, Box>::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, sym(1, 0)); + } else if (sym.size() == 3 && sym == "...") { + return Tree(std::in_place_index); + } else if (sym.size() == 1 && sym == "?") { + return Tree(std::in_place_index); + } else if (auto const &o = oprs.find(sym[0]); sym.size() == 1 && o != oprs.end()) { + return Tree(std::in_place_index, *o); + } else if (auto const &c = cmps.find(sym[0]); sym.size() == 1 && c != cmps.end()) { + return Tree(std::in_place_index, *c); + } else try { + return Tree(std::in_place_index, 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, Box>::make("T", Tree(std::in_place_index, Box>::make("F", Tree(std::in_place_index, "T"))))); + static const auto F = Tree(std::in_place_index, Box>::make("T", Tree(std::in_place_index, Box>::make("F", Tree(std::in_place_index, "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(&token)) { + auto &[fst, snd] = **papp; + fst.calc(); + if (auto pnil = std::get_if(&fst.token)) { + token.emplace(); + } else if (auto pchk = std::get_if(&fst.token)) { + snd.calc(); + *this = snd.token.index() == TokenIdx::Nil ? F : T; + } else if (auto plef = std::get_if(&fst.token)) { + auto &[par, tmp] = **plef; + tmp.substitute(std::make_shared>(std::move(snd), 0), std::move(par)); + *this = Tree(std::move(tmp)); + goto tail_call; + } else if (auto peef = std::get_if(&fst.token)) { + snd.calc(); + auto &[par, tmp] = **peef; + tmp.substitute(std::make_shared>(std::move(snd), 1), std::move(par)); + *this = Tree(std::move(tmp)); + goto tail_call; + } else if (auto popr = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&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(&snd.token)) { + token.emplace(); + } else { + throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate()); + } + } else if (auto pcmp = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&snd.token)) { + token = std::make_pair(std::move(*pcmp), std::move(*pint)); + } else if (auto pnil = std::get_if(&snd.token)) { + token.emplace(); + } else { + throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate()); + } + } else if (auto paoi = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&snd.token)) { + token = paoi->first.second(std::move(*pint), std::move(paoi->second)); + } else if (auto pnil = std::get_if(&snd.token)) { + token.emplace(); + } else { + throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate()); + } + } else if (auto paci = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&snd.token)) { + *this = paci->first.second(std::move(*pint), std::move(paci->second)) ? T : F; + } else if (auto pnil = std::get_if(&snd.token)) { + token.emplace(); + } 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(&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> const &arg, std::string const &tar) { + if (auto papp = std::get_if(&token)) { + auto &[fst, snd] = **papp; + fst.substitute(arg, tar); + snd.substitute(arg, tar); + } else if (auto plef = std::get_if(&token)) { + auto &[par, tmp] = **plef; + if (par != tar) { + tmp.substitute(arg, tar); + } + } else if (auto peef = std::get_if(&token)) { + auto &[par, tmp] = **peef; + if (par != tar) { + tmp.substitute(arg, tar); + } + } else if (auto ppar = std::get_if(&token)) { + if (*ppar == tar) { + token.emplace(arg); + } + } + } + void analyze(std::unordered_set &set) { + if (auto papp = std::get_if(&token)) { + auto &[fst, snd] = **papp; + fst.analyze(set); + snd.analyze(set); + } else if (auto plef = std::get_if(&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(&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(&token)) { + if (set.find(*ppar) == set.end()) { + if (auto const &itr = map.find(*ppar); itr != map.end()) { + token.emplace(itr->second); + } else { + throw std::runtime_error("unbound variable: $" + *ppar); + } + } + } + } + static inline std::unordered_map>> 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 flat; + flat.push(std::exchange(token, std::nullopt)); + for (; not flat.empty(); flat.pop()) { + auto &token = flat.front(); + if (auto papp = std::get_if(&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(&token)) { + auto &[par, tmp] = **parg; + flat.push(std::exchange(tmp.token, std::nullopt)); + } else if (auto parg = std::get_if(&token)) { + auto &[par, tmp] = **parg; + flat.push(std::exchange(tmp.token, std::nullopt)); + } else if (auto parg = std::get_if(&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 set; + res.analyze(set); + map.erase(par); + if (calc) { + clr_flag(); + res.calc(); + } + return map.emplace(par, std::make_shared>(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(&token)) { + return "..."; + } else if (auto pchk = std::get_if(&token)) { + return "?"; + } else if (auto plef = std::get_if(&token)) { + auto &[par, tmp] = **plef; + auto s = "\\" + par + " " + tmp.translate(0, rb && !rb); + return rb ? "(" + s + ")" : s; + } else if (auto peef = std::get_if(&token)) { + auto &[par, tmp] = **peef; + auto s = "^" + par + " " + tmp.translate(0, rb && !rb); + return rb ? "(" + s + ")" : s; + } else if (auto pint = std::get_if(&token)) { + return pint->to_string(); + } else if (auto popr = std::get_if(&token)) { + return std::string{popr->first}; + } else if (auto pcmp = std::get_if(&token)) { + return std::string{pcmp->first}; + } else if (auto paoi = std::get_if(&token)) { + auto s = std::string{paoi->first.first, ' '} + paoi->second.to_string(); + return lb ? "(" + s + ")" : s; + } else if (auto paci = std::get_if(&token)) { + auto s = std::string{paci->first.first, ' '} + paci->second.to_string(); + return lb ? "(" + s + ")" : s; + } else if (auto papp = std::get_if(&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(&token)) { + return (*parg)->first.translate(lb, rb); + } else if (auto ppar = std::get_if(&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; +} diff --git a/lambda_cbv.cpp b/lambda_cbv.cpp new file mode 100644 index 0000000..5a9fc4c --- /dev/null +++ b/lambda_cbv.cpp @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#elif defined __unix__ +#include +#include +#include +#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 const oprs = { + {'+', operator+}, + {'-', operator-}, + {'*', operator*}, + {'/', operator/}, + {'%', operator%}, +}; +static inline std::unordered_map 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, std::pair, BigInt>, + std::pair, std::pair, BigInt>, + Box>, Box>, + Box>, std::shared_ptr>, std::string>; + TokenVar token; + template >> + Tree(Args &&...args): token(std::forward(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>::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, Box>::make(sym(1, 0), parse(std::move(exp)))))); + } else if (sym[0] == '|') { + return parse(std::move(exp), Tree(std::in_place_index, Box>::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, Box>::make(sym(1, 0), parse(std::move(exp)))))); + } else if (sym[0] == '@') { + return parse(std::move(exp), Tree(std::in_place_index, Box>::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, sym(1, 0)); + } else if (sym[0] == '&') { + return Tree(std::in_place_index, sym(1, 0)); + } else if (sym.size() == 3 && sym == "...") { + return Tree(std::in_place_index); + } else if (sym.size() == 1 && sym == "?") { + return Tree(std::in_place_index); + } else if (auto const &o = oprs.find(sym[0]); sym.size() == 1 && o != oprs.end()) { + return Tree(std::in_place_index, *o); + } else if (auto const &c = cmps.find(sym[0]); sym.size() == 1 && c != cmps.end()) { + return Tree(std::in_place_index, *c); + } else try { + return Tree(std::in_place_index, 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, Box>::make("T", Tree(std::in_place_index, Box>::make("F", Tree(std::in_place_index, "T"))))); + static const auto F = Tree(std::in_place_index, Box>::make("T", Tree(std::in_place_index, Box>::make("F", Tree(std::in_place_index, "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(&token)) { + auto &[fst, snd] = **papp; + fst.calc(); + if (auto pnil = std::get_if(&fst.token)) { + token.emplace(); + } else if (auto pchk = std::get_if(&fst.token)) { + snd.calc(); + *this = snd.token.index() == TokenIdx::Nil ? F : T; + } else if (auto plef = std::get_if(&fst.token)) { + auto &[par, tmp] = **plef; + tmp.substitute(std::make_shared>(std::move(snd), 0), std::move(par)); + *this = Tree(std::move(tmp)); + goto tail_call; + } else if (auto peef = std::get_if(&fst.token)) { + snd.calc(); + auto &[par, tmp] = **peef; + tmp.substitute(std::make_shared>(std::move(snd), 1), std::move(par)); + *this = Tree(std::move(tmp)); + goto tail_call; + } else if (auto popr = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&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(&snd.token)) { + token.emplace(); + } else { + throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate()); + } + } else if (auto pcmp = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&snd.token)) { + token = std::make_pair(std::move(*pcmp), std::move(*pint)); + } else if (auto pnil = std::get_if(&snd.token)) { + token.emplace(); + } else { + throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate()); + } + } else if (auto paoi = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&snd.token)) { + token = paoi->first.second(std::move(*pint), std::move(paoi->second)); + } else if (auto pnil = std::get_if(&snd.token)) { + token.emplace(); + } else { + throw std::runtime_error("cannot apply " + fst.translate() + " on: " + snd.translate()); + } + } else if (auto paci = std::get_if(&fst.token)) { + snd.calc(); + if (auto pint = std::get_if(&snd.token)) { + *this = paci->first.second(std::move(*pint), std::move(paci->second)) ? T : F; + } else if (auto pnil = std::get_if(&snd.token)) { + token.emplace(); + } 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(&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(&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> const &arg, std::string const &tar) { + if (auto papp = std::get_if(&token)) { + auto &[fst, snd] = **papp; + fst.substitute(arg, tar); + snd.substitute(arg, tar); + } else if (auto plef = std::get_if(&token)) { + auto &[par, tmp] = **plef; + if (par != tar) { + tmp.substitute(arg, tar); + } + } else if (auto peef = std::get_if(&token)) { + auto &[par, tmp] = **peef; + if (par != tar) { + tmp.substitute(arg, tar); + } + } else if (auto ppar = std::get_if(&token)) { + if (*ppar == tar) { + token.emplace(arg); + } + } + } + void analyze(std::unordered_set &set) const { + if (auto papp = std::get_if(&token)) { + auto &[fst, snd] = **papp; + fst.analyze(set); + snd.analyze(set); + } else if (auto plef = std::get_if(&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(&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(&token)) { + if (set.find(*ppar) == set.end()) { + throw std::runtime_error("unbound variable: $" + *ppar); + } + } + } + static inline std::unordered_map 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 flat; + flat.push(std::exchange(token, std::nullopt)); + for (; not flat.empty(); flat.pop()) { + auto &token = flat.front(); + if (auto papp = std::get_if(&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(&token)) { + auto &[par, tmp] = **parg; + flat.push(std::exchange(tmp.token, std::nullopt)); + } else if (auto parg = std::get_if(&token)) { + auto &[par, tmp] = **parg; + flat.push(std::exchange(tmp.token, std::nullopt)); + } else if (auto parg = std::get_if(&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 set; + res.analyze(set); + clr_flag(); + res.calc(); + return res; + } + static void def(Slice &&exp, std::string const &glb) { + std::unordered_set 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(&token)) { + return "..."; + } else if (auto pchk = std::get_if(&token)) { + return "?"; + } else if (auto plef = std::get_if(&token)) { + auto &[par, tmp] = **plef; + auto s = "\\" + par + " " + tmp.translate(0, rb && !rb); + return rb ? "(" + s + ")" : s; + } else if (auto peef = std::get_if(&token)) { + auto &[par, tmp] = **peef; + auto s = "^" + par + " " + tmp.translate(0, rb && !rb); + return rb ? "(" + s + ")" : s; + } else if (auto pint = std::get_if(&token)) { + return pint->to_string(); + } else if (auto popr = std::get_if(&token)) { + return std::string{popr->first}; + } else if (auto pcmp = std::get_if(&token)) { + return std::string{pcmp->first}; + } else if (auto paoi = std::get_if(&token)) { + auto s = std::string{paoi->first.first, ' '} + paoi->second.to_string(); + return lb ? "(" + s + ")" : s; + } else if (auto paci = std::get_if(&token)) { + auto s = std::string{paci->first.first, ' '} + paci->second.to_string(); + return lb ? "(" + s + ")" : s; + } else if (auto papp = std::get_if(&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(&token)) { + return (*parg)->first.translate(lb, rb); + } else if (auto ppar = std::get_if(&token)) { + return "$" + *ppar; + } else if (auto pglb = std::get_if(&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; +} diff --git a/lambda_exp.cpp b/lambda_exp.cpp new file mode 100644 index 0000000..8d84f05 --- /dev/null +++ b/lambda_exp.cpp @@ -0,0 +1,453 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#elif defined __unix__ +#include +#include +#include +#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 const oprs = { + {'+', operator+}, + {'-', operator-}, + {'*', operator*}, + {'/', operator/}, + {'%', operator%}, +}; +static inline std::unordered_map 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, std::pair, BigInt>, + std::pair, std::pair, BigInt>, + std::pair, std::pair, + std::pair>; + std::shared_ptr 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, 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, sym(1, 0), parse(std::move(exp))))); + } else if (sym[0] == '|') { + return parse(std::move(exp), new TokenVar(std::in_place_index, 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, sym(1, 0), parse(std::move(exp))))); + } else if (sym[0] == '@') { + return parse(std::move(exp), new TokenVar(std::in_place_index, 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, sym(1, 0)); + } else if (sym.size() == 3 && sym == "...") { + return new TokenVar(std::in_place_index); + } else if (sym.size() == 1 && sym == "?") { + return new TokenVar(std::in_place_index); + } else if (auto const &o = oprs.find(sym[0]); sym.size() == 1 && o != oprs.end()) { + return new TokenVar(std::in_place_index, *o); + } else if (auto const &c = cmps.find(sym[0]); sym.size() == 1 && c != cmps.end()) { + return new TokenVar(std::in_place_index, *c); + } else try { + return new TokenVar(std::in_place_index, 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, "T", Tree(new TokenVar(std::in_place_index, "F", Tree(new TokenVar(std::in_place_index, "T")))))); + static const auto F = Tree(new TokenVar(std::in_place_index, "T", Tree(new TokenVar(std::in_place_index, "F", Tree(new TokenVar(std::in_place_index, "F")))))); + static const auto N = Tree(new TokenVar(std::in_place_index)); + 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(sp.get())) { + auto &[fst, snd] = *papp; + fst.calc(); + snd.lb = 1; + if (auto pnil = std::get_if(fst.sp.get())) { + *sp = *N.sp; + } else if (auto pchk = std::get_if(fst.sp.get())) { + snd.calc(); + *sp = snd.sp->index() == TokenIdx::Nil ? *F.sp : *T.sp; + } else if (auto plef = std::get_if(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(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(fst.sp.get())) { + snd.calc(); + if (auto pint = std::get_if(snd.sp.get()); pint && (*pint || popr->first != '/' && popr->first != '%')) { + *sp = std::make_pair(*popr, *pint); + } else if (auto pnil = std::get_if(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(fst.sp.get())) { + snd.calc(); + if (auto pint = std::get_if(snd.sp.get())) { + *sp = std::make_pair(*pcmp, *pint); + } else if (auto pnil = std::get_if(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(fst.sp.get())) { + snd.calc(); + if (auto pint = std::get_if(snd.sp.get())) { + *sp = paoi->first.second(*pint, paoi->second); + } else if (auto pnil = std::get_if(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(fst.sp.get())) { + snd.calc(); + if (auto pint = std::get_if(snd.sp.get())) { + *sp = paci->first.second(*pint, paci->second) ? *T.sp : *F.sp; + } else if (auto pnil = std::get_if(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(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, std::move(fsb), std::move(ssb)); + } + } else if (auto plef = std::get_if(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, par, std::move(tsb)); + } + } + } else if (auto peef = std::get_if(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, par, std::move(tsb)); + } + } + } else if (auto ppar = std::get_if(sp.get())) { + if (*ppar == tar) { + return arg; + } + } + return *this; + } + void analyze(std::unordered_set &set) { + if (auto papp = std::get_if(sp.get())) { + auto &[fst, snd] = *papp; + fst.analyze(set); + snd.analyze(set); + } else if (auto plef = std::get_if(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(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(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 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> 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(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(sp.get())) { + auto &[par, tmp] = *plef; + flat.push(std::move(tmp.sp)); + } else if (auto peef = std::get_if(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 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(sp.get())) { + return "..."; + } else if (auto pchk = std::get_if(sp.get())) { + return "?"; + } else if (auto plef = std::get_if(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(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(sp.get())) { + return pint->to_string(); + } else if (auto popr = std::get_if(sp.get())) { + return std::string{popr->first}; + } else if (auto pcmp = std::get_if(sp.get())) { + return std::string{pcmp->first}; + } else if (auto paoi = std::get_if(sp.get())) { + auto s = std::string{paoi->first.first, ' '} + paoi->second.to_string(); + return lb ? "(" + s + ")" : s; + } else if (auto paci = std::get_if(sp.get())) { + auto s = std::string{paci->first.first, ' '} + paci->second.to_string(); + return lb ? "(" + s + ")" : s; + } else if (auto papp = std::get_if(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(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; +} diff --git a/slice.hpp b/slice.hpp new file mode 100644 index 0000000..8dc71fa --- /dev/null +++ b/slice.hpp @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +class Slice { + std::shared_ptr arr; + char const *beg; + char const *end; + Slice(std::shared_ptr 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(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(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; + } +};