From a4635fb95235ba4bf077bd59957da0626fc5ba72 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 14 Dec 2021 12:34:06 -0500 Subject: EC, a terminal-based RPN calculator --- src/ui/terminal.cc | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/ui/terminal.cc (limited to 'src/ui/terminal.cc') diff --git a/src/ui/terminal.cc b/src/ui/terminal.cc new file mode 100644 index 0000000..ccb9c4d --- /dev/null +++ b/src/ui/terminal.cc @@ -0,0 +1,184 @@ +// Copyright 2021 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "src/ui/terminal.h" + +#include +#include +#include + +#include "src/builtin.h" +#include "src/error.h" +#include "src/language.h" +#include "src/parser_driver.h" +#include "src/ui/terminal/line.h" +#include "third_party/abseil/absl/strings/str_cat.h" +#include "third_party/abseil/absl/strings/str_join.h" +#include "third_party/abseil/absl/strings/string_view.h" + +namespace ec { + +int TerminalUi::Main() noexcept { + BlockSigwinch(); + TerminalLine tty; + + State machine_state; + std::string input_buffer; + while (true) { + tty.SetLineImmediately(absl::StrCat( + absl::StrJoin(machine_state.stack, " ", FormatStackElement), " > ", + input_buffer)); + + char c = tty.GetChar(); + if (c == tty.interrupt_char() || c == tty.quit_char()) { + // Somebody hit Ctrl-C or Ctrl-\. + return 0; + } + switch (c) { + case kControlD: + if (input_buffer.empty()) { + return 0; + } + tty.Beep(); + break; + + case kControlU: + input_buffer.clear(); + break; + + case '\n': + case '\r': + case ' ': + try { + std::vector> program; + if (input_buffer.empty()) { + program = {std::make_shared(BuiltinDup)}; + } else { + program = ParseFromString(input_buffer); + } + + State s = machine_state; + EvaluateAll(program, s); + machine_state = s; + input_buffer.clear(); + } catch (const Error& e) { + tty.Beep(); + } + break; + + case '\x7f': + try { + if (input_buffer.empty()) { + State s = machine_state; + ForeignProgramTerm(BuiltinDrop).Evaluate(s); + machine_state = s; + } else { + input_buffer.pop_back(); + } + } catch (const Error& e) { + tty.Beep(); + } + break; + + case '+': + case '-': + case '*': + case '/': + try { + std::vector> program = + ParseFromString(absl::StrCat(input_buffer, std::string(1, c))); + State s = machine_state; + EvaluateAll(program, s); + machine_state = s; + input_buffer.clear(); + } catch (const Error& e) { + tty.Beep(); + } + break; + + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + input_buffer.push_back(c); + break; + + default: + tty.Beep(); + break; + } + } +} + +} // namespace ec -- cgit v1.2.3