Added 'load from file' option(' operator)
dc / build (push) Successful in 13s
Details
dc / build (push) Successful in 13s
Details
This commit is contained in:
parent
dbecebce07
commit
4a71a91ee8
|
@ -46,7 +46,8 @@ Some of the supported features are:
|
|||
- Macros:
|
||||
- Define a new macro inside square brackets(`[ ]`);
|
||||
- Executing a macro from the stack(`x`);
|
||||
- Evaluate a macro by comparing top-of-head and second-of-head elements(`>X`, `<X`, `>=X`, `<=X`, `!=` where `X` is a register).
|
||||
- Evaluate a macro by comparing top-of-head and second-of-head elements(`>X`, `<X`, `>=X`, `<=X`, `!=` where `X` is a register);
|
||||
- Load external file(`'`).
|
||||
|
||||
And much more. You can find the complete manual [here](https://github.com/ice-bit/dc/blob/master/man.md).
|
||||
|
||||
|
|
16
man.md
16
man.md
|
@ -3,7 +3,7 @@ title: dc
|
|||
section: 1
|
||||
header: General Commands Manual
|
||||
footer: Marco Cetica
|
||||
date: March 12, 2024
|
||||
date: March 13, 2024
|
||||
---
|
||||
|
||||
|
||||
|
@ -385,6 +385,10 @@ Exit with return code `0`.
|
|||
|
||||
Reads a line from the terminal and executes it. This command allows a macro to request input from the user.
|
||||
|
||||
**'**
|
||||
|
||||
Pops a string off the stack, use it as a filepath, read its content and execute it.
|
||||
|
||||
# EXAMPLES
|
||||
Below, there are some practical problems solved using **dc**.
|
||||
|
||||
|
@ -469,6 +473,16 @@ lB -1 * lD + lA # POSITIVE DELTA
|
|||
[ Result in base 2: ] P R pb
|
||||
```
|
||||
|
||||
11. Load an external file:
|
||||
```sh
|
||||
$> echo "[ p 1 + d lN >=L ] sL" > loop.dc
|
||||
$> cat prg.dc
|
||||
[ loop.dc ] ' # Load loop macro
|
||||
[ Enter limit: ] P # Ask user for limit 'N'
|
||||
? 1 + sN # Read from stdin
|
||||
c 1 lL x # Clear the stack, add lower bound, load and execute macro
|
||||
```
|
||||
|
||||
# AUTHORS
|
||||
The original version of the **dc** command was written by Robert Morris and Lorinda Cherry.
|
||||
This version of **dc** is developed by Marco Cetica.
|
||||
|
|
|
@ -65,6 +65,7 @@ void Evaluate::init_environment() {
|
|||
// Macro operations
|
||||
this->op_factory.emplace("x", MAKE_UNIQUE_PTR(Macro, OPType::EX));
|
||||
this->op_factory.emplace("?", MAKE_UNIQUE_PTR(Macro, OPType::RI));
|
||||
this->op_factory.emplace("'", MAKE_UNIQUE_PTR(Macro, OPType::LF));
|
||||
}
|
||||
|
||||
std::optional<std::string> Evaluate::eval() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <sstream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <fstream>
|
||||
|
||||
#include "adt.cpp"
|
||||
#include "eval.h"
|
||||
|
@ -15,6 +16,7 @@ std::optional<std::string> Macro::exec(dc::Stack<std::string> &stack, dc::Parame
|
|||
case OPType::EX: err = fn_execute(stack, parameters, regs); break;
|
||||
case OPType::CMP: err = fn_evaluate_macro(stack, parameters, regs); break;
|
||||
case OPType::RI: err = fn_read_input(stack, parameters, regs); break;
|
||||
case OPType::LF: err = fn_evaluate_file(stack, parameters, regs); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
@ -150,7 +152,7 @@ std::optional<std::string> Macro::fn_read_input(dc::Stack<std::string> &stack, d
|
|||
// Read user input from stdin
|
||||
std::string user_input;
|
||||
|
||||
std::cin >> user_input;
|
||||
std::getline(std::cin, user_input);
|
||||
if(std::cin.fail()) {
|
||||
return "Error while reading from stdin";
|
||||
}
|
||||
|
@ -167,6 +169,63 @@ std::optional<std::string> Macro::fn_read_input(dc::Stack<std::string> &stack, d
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Macro::fn_evaluate_file(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) {
|
||||
// Check whether the main stack has enough elements
|
||||
if(stack.empty()) {
|
||||
return "This operation does not work on empty stack";
|
||||
}
|
||||
|
||||
// If the head of the stack is a string,
|
||||
auto file_name = stack.pop(false);
|
||||
if(!is_num<double>(file_name)) {
|
||||
// Pop it from the stack
|
||||
stack.copy_xyz();
|
||||
stack.pop(true);
|
||||
// And use it as a filename
|
||||
std::fstream source_file(file_name, std::ios::in | std::ios::binary);
|
||||
if(source_file.fail()) {
|
||||
return std::string("Cannot open source file \"") + file_name + std::string("\"");
|
||||
}
|
||||
|
||||
// Read whole file into a buffer
|
||||
std::stringstream buf;
|
||||
buf << source_file.rdbuf();
|
||||
|
||||
// Execute file line by line
|
||||
std::string line;
|
||||
while(std::getline(buf, line, '\n')) {
|
||||
// Ignore comments or empty lines
|
||||
if(line.empty() || line.starts_with('#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove inline comments
|
||||
auto comment_pos = line.find('#');
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
if(comment_pos != std::string::npos) {
|
||||
// Convert only the first part of the line
|
||||
tokens = split(line.substr(0, comment_pos));
|
||||
} else {
|
||||
// Otherwise split the whole string
|
||||
tokens = split(line);
|
||||
}
|
||||
|
||||
// Evaluate expression
|
||||
Evaluate evaluator(tokens, regs, stack, parameters);
|
||||
auto err = evaluator.eval();
|
||||
// Handle errors
|
||||
if(err != std::nullopt) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "This operation requires string values";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string> Macro::split(const std::string& str) {
|
||||
std::stringstream ss(str);
|
||||
std::istream_iterator<std::string> begin(ss);
|
||||
|
|
|
@ -17,6 +17,7 @@ private:
|
|||
static std::optional<std::string> fn_execute(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||
std::optional<std::string> fn_evaluate_macro(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||
static std::optional<std::string> fn_read_input(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||
static std::optional<std::string> fn_evaluate_file(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||
|
||||
OPType op_type;
|
||||
Operator op{};
|
||||
|
|
|
@ -17,5 +17,5 @@ enum class OPType {
|
|||
PCG, P, PBB, PBH, PBO, CLR, PH, SO, DP, PS, CH, CS,
|
||||
SP, GP, SOR, GOR, SIR, GIR, LX, LY, LZ,
|
||||
// Macro operations
|
||||
EX, CMP, RI
|
||||
EX, CMP, RI, LF
|
||||
};
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/sh
|
||||
|
||||
tearup() {
|
||||
cat <<EOF > test_lfile.dc
|
||||
[ 5 d ! + ] sX # Computes 5! + 5
|
||||
EOF
|
||||
}
|
||||
|
||||
teardown() {
|
||||
rm test_lfile.dc
|
||||
}
|
||||
|
||||
utest() {
|
||||
PROGRAM="$PWD/build/dc"
|
||||
tearup
|
||||
|
||||
# Test empty stack
|
||||
EXPECTED="This operation does not work on empty stack"
|
||||
ACTUAL=$("$PROGRAM" -e "'" 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test non string values
|
||||
EXPECTED="This operation requires string values"
|
||||
ACTUAL=$("$PROGRAM" -e "[ 5 ] '" 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test file not found
|
||||
EXPECTED="Cannot open source file \"foo.dc\""
|
||||
ACTUAL=$("$PROGRAM" -e "[ foo.dc ] '" 2>&1) || true
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
# Test normal behaviour
|
||||
EXPECTED="125"
|
||||
ACTUAL=$("$PROGRAM" -e "[ test_lfile.dc ] ' lX x p")
|
||||
assert_eq "$EXPECTED" "$ACTUAL"
|
||||
|
||||
teardown
|
||||
}
|
||||
# vim: ts=4 sw=4 softtabstop=4 expandtab:
|
Loading…
Reference in New Issue