mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2025-07-06 11:24:03 +00:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
432b9b22ab | |||
97260af061 | |||
91590e92aa | |||
6199944350 | |||
f799eb6d81 | |||
298c601300 | |||
ce1a36eba5 | |||
a90ca3c849 | |||
fdf33c69b7 | |||
6f2b59c4d6 | |||
fd94247aec | |||
8e54c6519c | |||
0c1419ec98 | |||
225c3b8ebd | |||
da24b85f68 | |||
f25a30edc7 | |||
e8c0080f77 | |||
b8cada73e8 | |||
be1c6c14fa | |||
a15d2bf257 | |||
87b2cc0902 | |||
5d95f10dd8 | |||
9d4340b3ac | |||
45df4524ea | |||
474b10ccaa | |||
ed96470e18 | |||
7b99997f28 | |||
42f9552352 | |||
fa804e91f0 | |||
0ec15bcd63 | |||
06e2e1f309 | |||
32c72cb89a | |||
f125b533f7 | |||
f6c5478063 | |||
efd51bcb48 | |||
33dd3228f8 | |||
548880a517 | |||
1da6c3e9da | |||
bdba86249e | |||
87f5398649 | |||
811c8b976a | |||
0422976719 | |||
2893f7fe79 | |||
34a4f431c0 | |||
f3db9b7cc0 |
36
.github/workflows/cmake-macos.yml
vendored
Normal file
36
.github/workflows/cmake-macos.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: CMake MacOS
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Brew
|
||||
run: brew install ragel
|
||||
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: Archive
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mpw fat
|
||||
path: ${{runner.workspace}}/build/mpw-shell
|
29
.github/workflows/cmake-ubuntu.yml
vendored
Normal file
29
.github/workflows/cmake-ubuntu.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: CMake Ubuntu
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: apt-get
|
||||
run: sudo apt-get install ragel libreadline-dev
|
||||
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE
|
@ -14,5 +14,5 @@ script: make
|
||||
before_install:
|
||||
- brew update
|
||||
- brew install ragel
|
||||
- brew install "https://github.com/ksherlock/lemon--/raw/master/lemon++.rb"
|
||||
- brew install "https://raw.githubusercontent.com/ksherlock/homebrew-ksherlock/master/lemonxx.rb"
|
||||
|
||||
|
@ -1,25 +1,50 @@
|
||||
|
||||
# CMAKE_INSTALL_PREFIX defaults to /usr/local.
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project("mpw-shell")
|
||||
set (PROJECT_TYPE "CXX")
|
||||
set (PROJECT_NAME "MPW Shell")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-g -Wall -Wno-unused-variable -Wno-multichar -O1")
|
||||
|
||||
# -std=c++14
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_EXTENSIONS FALSE)
|
||||
|
||||
|
||||
#
|
||||
# build config.h
|
||||
#
|
||||
|
||||
INCLUDE (CheckFunctionExists)
|
||||
INCLUDE (CheckLibraryExists)
|
||||
|
||||
SET(CMAKE_EXTRA_INCLUDE_FILES stdio.h)
|
||||
CHECK_FUNCTION_EXISTS(dprintf HAVE_DPRINTF)
|
||||
SET(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
CHECK_LIBRARY_EXISTS(edit readline "" HAVE_LIBEDIT)
|
||||
CHECK_LIBRARY_EXISTS(readline readline "" HAVE_LIBREADLINE)
|
||||
CHECK_LIBRARY_EXISTS(history add_history "" HAVE_LIBHISTORY)
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
|
||||
add_compile_options(-g -Wall -Wno-unused-variable -Wno-multichar -O1)
|
||||
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-const-variable ")
|
||||
add_compile_options(-Wno-unused-const-variable)
|
||||
endif()
|
||||
|
||||
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-variable")
|
||||
add_compile_options(-Wno-unused-but-set-variable)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
add_definitions(-I ${CMAKE_SOURCE_DIR}/ -I ${CMAKE_CURRENT_BINARY_DIR}/ )
|
||||
include_directories(${CMAKE_SOURCE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# from https://github.com/gsauthof/cmake-ragel
|
||||
macro(RAGEL_TARGET Name Input Output)
|
||||
@ -64,18 +89,29 @@ RAGEL_TARGET(value value.rl value.cpp COMPILE_FLAGS "-p -G2")
|
||||
|
||||
|
||||
# need to copy all OUTPUT file to the build dir
|
||||
add_custom_command(
|
||||
OUTPUT phase2-parser.cpp phase2-parser.h phase2-parser.out
|
||||
COMMAND lemon++ phase2-parser.lemon
|
||||
COMMAND mv -f phase2-parser.cpp phase2-parser.h phase2-parser.out ${CMAKE_CURRENT_BINARY_DIR}/
|
||||
MAIN_DEPENDENCY phase2-parser.lemon
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
# add_custom_command(
|
||||
# OUTPUT phase3.cpp phase3.h phase3.out
|
||||
# COMMAND lemon++ phase3.lemon
|
||||
# COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/
|
||||
# MAIN_DEPENDENCY phase3.lemon
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
# )
|
||||
|
||||
find_program(LEMON_PLUSPLUS NAMES lemon++)
|
||||
if (LEMON_PLUSPLUS)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/phase3.cpp ${CMAKE_CURRENT_SOURCE_DIR}/phase3.h
|
||||
#COMMAND cp -f "${CMAKE_CURRENT_SOURCE_DIR}/parser.lemon" "parser.lemon"
|
||||
COMMAND lemon++ phase3.lemon
|
||||
MAIN_DEPENDENCY phase3.lemon
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
||||
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
|
||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp
|
||||
mpw-shell-parser.cpp mpw_parser.cpp value.cpp mpw-shell-quote.cpp mpw-regex.cpp
|
||||
phase1.cpp phase2.cpp phase3.cpp command.cpp environment.cpp builtins.cpp
|
||||
pathnames.cpp
|
||||
macroman.cpp
|
||||
cxx/mapped_file.cpp
|
||||
@ -84,12 +120,19 @@ add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
||||
cxx/directory_iterator.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(mpw-shell edit)
|
||||
#
|
||||
# -ledit includes history stuff. gnu -lreadline does not.
|
||||
#
|
||||
if(HAVE_LIBEDIT)
|
||||
target_link_libraries(mpw-shell edit)
|
||||
elseif(HAVE_LIBREADLINE)
|
||||
target_link_libraries(mpw-shell readline)
|
||||
if (HAVE_LIBHISTORY)
|
||||
target_link_libraries(mpw-shell history)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# all this for -std=c++14
|
||||
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD 14)
|
||||
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD_REQUIRED TRUE)
|
||||
set_property (TARGET mpw-shell PROPERTY CXX_EXTENSIONS FALSE)
|
||||
|
||||
# create a symlink for mpw-make
|
||||
add_custom_command(
|
||||
|
45
README.md
45
README.md
@ -11,16 +11,17 @@ Supported features
|
||||
* Begin ... End
|
||||
* Loop ... End
|
||||
* For name In [word...] ... End
|
||||
* Break [If], Continue [If]
|
||||
* Break [If], Continue [If], Exit [If]
|
||||
* ( ... )
|
||||
* ||
|
||||
* &&
|
||||
* redirection
|
||||
* | "pipes" (via a temporary file. Presumably, that's what MPW did as well.)
|
||||
* Redirection
|
||||
* | "pipes" (via a temporary file. Presumably, that's what MPW did as well.)
|
||||
* Subshells (`...`, ``...``)
|
||||
|
||||
|
||||
Not (yet) supported
|
||||
-------------
|
||||
* subshells (`...`, ``...``)
|
||||
* aliases
|
||||
* regular expressions
|
||||
* text-editing commands (search forward/backward, et cetera)
|
||||
@ -33,9 +34,11 @@ Builtin Commands
|
||||
* Directory
|
||||
* Echo
|
||||
* Evaluate
|
||||
* Execute
|
||||
* Exists
|
||||
* Export
|
||||
* Parameters
|
||||
* Quit
|
||||
* Quote
|
||||
* Set
|
||||
* Shift
|
||||
@ -49,7 +52,39 @@ Builtin Commands
|
||||
Setup
|
||||
-----
|
||||
1. Install MPW. The mpw binary should be somewhere in your `$PATH`.
|
||||
It also checks `/usr/local/bin/mpw` and `$HOME/mpw/bin/mpw`
|
||||
It also checks `/usr/local/bin/mpw` and `$HOME/mpw/bin/mpw`. You can
|
||||
use mpw-shell without it but only with builtin commands.
|
||||
2. Copy the `Startup` script to `$HOME/mpw/`. This script is executed
|
||||
when mpw-shell (or mpw-make) starts up (imagine that) and should
|
||||
be used to set environment variables.
|
||||
|
||||
|
||||
Command Line Flags
|
||||
------------------
|
||||
|
||||
-D name=value Define environment variable
|
||||
-v Be verbose (equivalent to -Decho=1)
|
||||
-f Ignore the Startup script
|
||||
-c string Execute string
|
||||
-h Display help
|
||||
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
Standard CMake build sequence:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
After that, do the standard CMake install sequence in the same folder:
|
||||
|
||||
```bash
|
||||
cmake --install
|
||||
```
|
||||
|
||||
to install `mpw-shell` and `mpw-make` in `/usr/bin/local`.
|
||||
|
329
builtins.cpp
329
builtins.cpp
@ -4,6 +4,7 @@
|
||||
#include "fdset.h"
|
||||
#include "value.h"
|
||||
#include "environment.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -16,15 +17,18 @@
|
||||
#include <cstdarg>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "cxx/string_splitter.h"
|
||||
#include "cxx/filesystem.h"
|
||||
#include "cxx/mapped_file.h"
|
||||
|
||||
//MAXPATHLEN
|
||||
#include <sys/param.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace ToolBox {
|
||||
std::string MacToUnix(const std::string path);
|
||||
@ -114,16 +118,23 @@ namespace {
|
||||
#define fputc DO_NOT_USE_FPUTC
|
||||
|
||||
inline int fdputs(const char *data, int fd) {
|
||||
auto rv = write(fd, data, strlen(data)); return rv < 0 ? EOF : rv;
|
||||
auto rv = write(fd, data, strlen(data));
|
||||
return rv < 0 ? EOF : rv;
|
||||
}
|
||||
|
||||
inline int fdputs(const std::string &s, int fd) {
|
||||
auto rv = write(fd, s.data(), s.size());
|
||||
return rv < 0 ? EOF : rv;
|
||||
}
|
||||
|
||||
inline int fdputc(int c, int fd) {
|
||||
unsigned char tmp = c;
|
||||
auto rv = write(fd, &tmp, 1); return rv < 0 ? EOF : c;
|
||||
auto rv = write(fd, &tmp, 1);
|
||||
return rv < 0 ? EOF : c;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DPRINTF
|
||||
#define fdprintf(...) dprintf(__VA_ARGS__)
|
||||
#define fdprintf dprintf
|
||||
#else
|
||||
inline int fdprintf(int fd, const char *format, ...) {
|
||||
char *cp = nullptr;
|
||||
@ -177,7 +188,8 @@ int builtin_shift(Environment &env, const std::vector<std::string> &tokens, cons
|
||||
}
|
||||
|
||||
if (n == 0) return 0;
|
||||
|
||||
env.shift(n);
|
||||
#if 0
|
||||
auto argv = load_argv(env);
|
||||
if (argv.empty()) return 0;
|
||||
|
||||
@ -188,7 +200,7 @@ int builtin_shift(Environment &env, const std::vector<std::string> &tokens, cons
|
||||
} while (--n);
|
||||
|
||||
env.set_argv(argv);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -271,22 +283,14 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
|
||||
|
||||
const char *name = export_or_unexport ? "Export" : "Unexport";
|
||||
|
||||
struct {
|
||||
int _r = 0;
|
||||
int _s = 0;
|
||||
} flags;
|
||||
bool _r = false;
|
||||
bool _s = false;
|
||||
bool error = false;
|
||||
|
||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||
switch(c) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
flags._r = true;
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
flags._s = true;
|
||||
break;
|
||||
auto argv = getopt(tokens, [&](char c){
|
||||
switch(tolower(c)) {
|
||||
case 'r': _r = true; break;
|
||||
case 's': _s = true; break;
|
||||
default:
|
||||
fdprintf(stderr, "### %s - \"-%c\" is not an option.\n", name, c);
|
||||
error = true;
|
||||
@ -300,7 +304,7 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
|
||||
}
|
||||
|
||||
if (argv.empty()) {
|
||||
if (flags._r && flags._s) goto conflict;
|
||||
if (_r && _s) goto conflict;
|
||||
|
||||
// list of exported vars.
|
||||
// -r will generate unexport commands for exported variables.
|
||||
@ -312,14 +316,14 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
|
||||
for (const auto &kv : env) {
|
||||
const std::string& vname = kv.first;
|
||||
if (kv.second == export_or_unexport)
|
||||
fdprintf(stdout, "%s%s\n", flags._s ? "" : name, quote(vname).c_str());
|
||||
fdprintf(stdout, "%s%s\n", _s ? "" : name, quote(vname).c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
// mark as exported.
|
||||
|
||||
if (flags._r || flags._s) goto conflict;
|
||||
if (_r || _s) goto conflict;
|
||||
|
||||
for (std::string s : argv) {
|
||||
auto iter = env.find(s);
|
||||
@ -501,12 +505,9 @@ int builtin_directory(Environment &env, const std::vector<std::string> &tokens,
|
||||
bool error = false;
|
||||
|
||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||
switch(c)
|
||||
switch(tolower(c))
|
||||
{
|
||||
case 'q':
|
||||
case 'Q':
|
||||
q = true;
|
||||
break;
|
||||
case 'q': q = true; break;
|
||||
default:
|
||||
fdprintf(stderr, "### Directory - \"-%c\" is not an option.\n", c);
|
||||
error = true;
|
||||
@ -577,24 +578,12 @@ int builtin_exists(Environment &env, const std::vector<std::string> &tokens, con
|
||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||
switch(tolower(c))
|
||||
{
|
||||
case 'a':
|
||||
_a = true;
|
||||
break;
|
||||
case 'd':
|
||||
_d = true;
|
||||
break;
|
||||
case 'f':
|
||||
_f = true;
|
||||
break;
|
||||
case 'n':
|
||||
_n = true;
|
||||
break;
|
||||
case 'q':
|
||||
_q = true;
|
||||
break;
|
||||
case 'w':
|
||||
_w = true;
|
||||
break;
|
||||
case 'a': _a = true; break;
|
||||
case 'd': _d = true; break;
|
||||
case 'f': _f = true; break;
|
||||
case 'n': _n = true; break;
|
||||
case 'q': _q = true; break;
|
||||
case 'w': _w = true; break;
|
||||
default:
|
||||
fdprintf(stderr, "### Exists - \"-%c\" is not an option.\n", c);
|
||||
error = true;
|
||||
@ -703,7 +692,7 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
|
||||
tokens.pop_back();
|
||||
tokens.pop_back();
|
||||
|
||||
int32_t i = evaluate_expression("Evaluate", std::move(tokens));
|
||||
int32_t i = evaluate_expression(env, "Evaluate", std::move(tokens));
|
||||
|
||||
switch(type) {
|
||||
case '=':
|
||||
@ -733,7 +722,7 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
|
||||
}
|
||||
}
|
||||
|
||||
int32_t i = evaluate_expression("Evaluate", std::move(tokens));
|
||||
int32_t i = evaluate_expression(env, "Evaluate", std::move(tokens));
|
||||
|
||||
if (output == 'h') {
|
||||
fdprintf(stdout, "0x%08x\n", i);
|
||||
@ -765,15 +754,15 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
|
||||
int builtin_which(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||
|
||||
// which [-a] [-p] [command]
|
||||
bool a = false;
|
||||
bool p = false;
|
||||
bool _a = false;
|
||||
bool _p = false;
|
||||
bool error = false;
|
||||
|
||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||
switch(c)
|
||||
switch(tolower(c))
|
||||
{
|
||||
case 'a': case 'A': a = true; break;
|
||||
case 'p': case 'P': p = true; break;
|
||||
case 'a': _a = true; break;
|
||||
case 'p': _p = true; break;
|
||||
|
||||
default:
|
||||
fdprintf(stderr, "### Which - \"-%c\" is not an option.\n", c);
|
||||
@ -824,7 +813,7 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
||||
}
|
||||
|
||||
for(; ss; ++ss) {
|
||||
if (p) fdprintf(stderr, "checking %s\n", ss->c_str());
|
||||
if (_p) fdprintf(stderr, "checking %s\n", ss->c_str());
|
||||
|
||||
std::error_code ec;
|
||||
fs::path p(ToolBox::MacToUnix(ss->c_str()));
|
||||
@ -833,13 +822,13 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
||||
if (fs::exists(p, ec)) {
|
||||
found = true;
|
||||
fdprintf(stdout, "%s\n", quote(p).c_str());
|
||||
if (!a) break;
|
||||
if (!_a) break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check builtins...
|
||||
if (!found || a) {
|
||||
if (!found || _a) {
|
||||
|
||||
static const char *builtins[] = {
|
||||
"aboutbox",
|
||||
@ -847,12 +836,16 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
||||
"catenate",
|
||||
"directory",
|
||||
"echo",
|
||||
"execute",
|
||||
"exists",
|
||||
"export",
|
||||
"help",
|
||||
"false", // not in MPW
|
||||
"parameters",
|
||||
"quote",
|
||||
"set",
|
||||
"shift",
|
||||
"true", // not in MPW
|
||||
"unalias",
|
||||
"unexport",
|
||||
"unset",
|
||||
@ -940,9 +933,7 @@ int builtin_version(Environment &env, const std::vector<std::string> &tokens, co
|
||||
auto argv = getopt(tokens, [&](char c){
|
||||
switch(tolower(c))
|
||||
{
|
||||
case 'v':
|
||||
_v = true;
|
||||
break;
|
||||
case 'v': _v = true; break;
|
||||
default:
|
||||
fdprintf(stderr, "### Version - \"-%c\" is not an option.\n", c);
|
||||
error = true;
|
||||
@ -1026,3 +1017,225 @@ int builtin_true(Environment &, const std::vector<std::string> &, const fdmask &
|
||||
int builtin_false(Environment &, const std::vector<std::string> &, const fdmask &) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int builtin_quit(Environment &, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||
|
||||
bool error = false;
|
||||
bool _y = false;
|
||||
bool _n = false;
|
||||
bool _c = false;
|
||||
|
||||
auto argv = getopt(tokens, [&](char c){
|
||||
switch(tolower(c))
|
||||
{
|
||||
case 'c': _c = true; break;
|
||||
case 'n': _n = true; break;
|
||||
case 'y': _y = true; break;
|
||||
|
||||
default:
|
||||
fdprintf(stderr, "### Quit - \"-%c\" is not an option.\n", c);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (_y + _n + _c > 1) {
|
||||
fdprintf(stderr, "### Quit - Conflicting options were specified.\n");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (argv.size() > 0) {
|
||||
fdprintf(stderr, "### Quit - Too many parameters were specified.\n");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
fdprintf(stderr, "# Usage - Quit [-y | -n | -c]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
throw quit_command_t{};
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
template<class Iter>
|
||||
Iter find_entry(Iter begin, Iter end) {
|
||||
for(;;) {
|
||||
begin = std::find(begin, end, '\n');
|
||||
if (begin == end) return end;
|
||||
if (std::distance(begin, end) < 3) return end;
|
||||
++begin;
|
||||
if (begin[0] == '-' && begin[1] == '\n') return begin;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Iter1, class Iter2>
|
||||
bool help_name_match(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) {
|
||||
|
||||
if (std::distance(begin2, end2) <= std::distance(begin1, end1)) return false;
|
||||
|
||||
for( ; begin1 != end1; ++begin1, ++begin2) {
|
||||
if (tolower(*begin1) != tolower(*begin2)) return false;
|
||||
}
|
||||
|
||||
if (isspace(*begin2)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
bool help_helper(const mapped_file &f, const fdmask &fds, const std::string &cmd) {
|
||||
|
||||
/*
|
||||
* format is:
|
||||
* -\n
|
||||
* name whitespace
|
||||
* ....
|
||||
* -\n
|
||||
*/
|
||||
|
||||
if (f.size() < 2) return false;
|
||||
auto iter = f.begin();
|
||||
auto end = f.end();
|
||||
|
||||
if (iter[0] != '-' && iter[1] != '\n') {
|
||||
iter = find_entry(iter, end);
|
||||
}
|
||||
|
||||
if (cmd.empty()) {
|
||||
// print first entry
|
||||
write(stdout, f.begin(), std::distance(f.begin(), iter));
|
||||
fdputs("\n", stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
|
||||
if (iter == end) return false;
|
||||
|
||||
iter += 2;
|
||||
auto next = find_entry(iter, end);
|
||||
|
||||
auto l = std::distance(iter, end);
|
||||
if (help_name_match(cmd.begin(), cmd.end(), iter, next)) {
|
||||
|
||||
write(stdout, iter, std::distance(iter, next));
|
||||
fdputs("\n", stdout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int builtin_help(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||
|
||||
bool error = false;
|
||||
filesystem::path _f;
|
||||
|
||||
// todo -- -f to specify help file.
|
||||
auto argv = getopt(tokens, [&](char c){
|
||||
switch(tolower(c))
|
||||
{
|
||||
|
||||
default:
|
||||
fdprintf(stderr, "### Help - \"-%c\" is not an option.\n", c);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
fdputs("# Usage - Help [-f helpfile] command...\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
const filesystem::path sd(ToolBox::MacToUnix(env.get("shelldirectory")));
|
||||
const filesystem::path hd = sd / "Help";
|
||||
|
||||
filesystem::path mono;
|
||||
mapped_file mono_file;
|
||||
std::error_code ec;
|
||||
|
||||
if (_f.empty()) {
|
||||
mono = sd / "MPW.Help";
|
||||
mono_file = mapped_file(mono, mapped_file::priv, ec);
|
||||
} else {
|
||||
|
||||
mono = _f;
|
||||
mono_file = mapped_file(mono, mapped_file::priv, ec);
|
||||
if (!mono_file && !_f.is_absolute()) {
|
||||
mono = sd / _f;
|
||||
mono_file = mapped_file(mono, mapped_file::priv, ec);
|
||||
}
|
||||
|
||||
if (!mono_file) {
|
||||
fdprintf(stderr, "### Help: Unable to open %s\n", _f.c_str());
|
||||
fdprintf(stderr, "# %s\n", ec.message().c_str());
|
||||
return 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mono_file) {
|
||||
std::replace(mono_file.begin(), mono_file.end(), '\r', '\n');
|
||||
}
|
||||
|
||||
if (argv.empty()) {
|
||||
help_helper(mono_file, fds, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rv = 0;
|
||||
for (const auto &cmd : argv) {
|
||||
|
||||
|
||||
// 1. check for $MPW:Help:command
|
||||
filesystem::path p(hd);
|
||||
p /= cmd;
|
||||
|
||||
mapped_file f(p, mapped_file::priv, ec);
|
||||
if (!ec) {
|
||||
std::replace(f.begin(), f.end(), '\r', '\n');
|
||||
write(stdout, f.data(), f.size());
|
||||
fdputs("\n", stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (mono_file) {
|
||||
bool ok = help_helper(mono_file, fds, cmd);
|
||||
if (ok) break;
|
||||
}
|
||||
|
||||
fdprintf(stderr, "### Help - \"%s\" was not found.\n", cmd.c_str());
|
||||
rv = 2;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
int builtin_execute(Environment &e, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||
|
||||
// runs argv[1] in the current environment. unlike MPW, argv[1] must be a script.
|
||||
// special enhancement -- '-' means execute stdin.
|
||||
|
||||
if (tokens.size() < 2) return 0;
|
||||
std::string filename = tokens[1];
|
||||
if (filename == "-") {
|
||||
// since we're parsing stdin, don't let any children read it. [???]
|
||||
fdset new_fds;
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
new_fds.set(0, fd);
|
||||
|
||||
return read_fd(e, fds[0], new_fds | fds);
|
||||
}
|
||||
|
||||
return read_file(e, filename, fds);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ int builtin_directory(Environment &e, const std::vector<std::string> &, const fd
|
||||
int builtin_echo(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_exists(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_export(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_help(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_parameters(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_quote(Environment &e, const std::vector<std::string> &tokens, const fdmask &);
|
||||
int builtin_set(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
@ -25,6 +26,12 @@ int builtin_which(Environment &e, const std::vector<std::string> &, const fdmask
|
||||
int builtin_alias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_unalias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
|
||||
int builtin_execute(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_true(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_false(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
int builtin_quit(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||
|
||||
|
||||
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);
|
||||
|
||||
#endif
|
386
command.cpp
386
command.cpp
@ -1,10 +1,11 @@
|
||||
#include "command.h"
|
||||
#include "phase2-parser.h"
|
||||
#include "phase3.h"
|
||||
#include "environment.h"
|
||||
#include "fdset.h"
|
||||
#include "builtins.h"
|
||||
#include "mpw-shell.h"
|
||||
#include "error.h"
|
||||
#include "value.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
@ -17,6 +18,7 @@
|
||||
#include "cxx/filesystem.h"
|
||||
#include "cxx/string_splitter.h"
|
||||
|
||||
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
@ -24,6 +26,12 @@
|
||||
#include <signal.h>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
extern std::atomic<int> control_c;
|
||||
|
||||
namespace fs = filesystem;
|
||||
@ -40,8 +48,7 @@ typedef std::vector<token> token_vector;
|
||||
|
||||
namespace {
|
||||
|
||||
struct break_command_t {};
|
||||
struct continue_command_t {};
|
||||
|
||||
|
||||
/*
|
||||
* returns:
|
||||
@ -56,6 +63,12 @@ namespace {
|
||||
return -3;
|
||||
}
|
||||
|
||||
int bad_exit() {
|
||||
fprintf(stderr, "### Exit - Missing if keyword.\n");
|
||||
fprintf(stderr, "# Usage - Exit [Number] [if expression...]\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
int evaluate(int type, token_vector &&tokens, Environment &env) {
|
||||
std::reverse(tokens.begin(), tokens.end());
|
||||
|
||||
@ -64,6 +77,8 @@ namespace {
|
||||
switch(type) {
|
||||
default: return 0;
|
||||
|
||||
// exit [number] [if expr] ([number has been removed])
|
||||
case EXIT:
|
||||
case BREAK:
|
||||
case CONTINUE:
|
||||
case ELSE:
|
||||
@ -77,14 +92,16 @@ namespace {
|
||||
case BREAK: name = "Break"; break;
|
||||
case CONTINUE: name = "Continue"; break;
|
||||
case ELSE: name = "Else"; break;
|
||||
case EXIT: name = "Exit"; return bad_exit(); break;
|
||||
}
|
||||
return bad_if(name);
|
||||
}
|
||||
// fall through.
|
||||
|
||||
case IF:
|
||||
tokens.pop_back();
|
||||
try {
|
||||
e = evaluate_expression("If", std::move(tokens));
|
||||
e = evaluate_expression(env, "If", std::move(tokens));
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
fprintf(stderr, "%s\n", ex.what());
|
||||
@ -96,6 +113,23 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool is_script(const fs::path &path) {
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* check for a file type of TEXT */
|
||||
|
||||
uint8_t finfo[32];
|
||||
int ok = getxattr(path.c_str(), XATTR_FINDERINFO_NAME,finfo, sizeof(finfo), 0, 0);
|
||||
if (ok < 4) return false;
|
||||
|
||||
if (memcmp(finfo, "TEXT", 4) == 0) return true;
|
||||
return false;
|
||||
|
||||
#else
|
||||
return path.extension() == ".script";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -198,9 +232,12 @@ namespace {
|
||||
{"catenate", builtin_catenate},
|
||||
{"directory", builtin_directory},
|
||||
{"echo", builtin_echo},
|
||||
{"execute", builtin_execute},
|
||||
{"exists", builtin_exists},
|
||||
{"export", builtin_export},
|
||||
{"help", builtin_help},
|
||||
{"parameters", builtin_parameters},
|
||||
{"quit", builtin_quit},
|
||||
{"quote", builtin_quote},
|
||||
{"set", builtin_set},
|
||||
{"shift", builtin_shift},
|
||||
@ -209,6 +246,10 @@ namespace {
|
||||
{"unset", builtin_unset},
|
||||
{"version", builtin_version},
|
||||
{"which", builtin_which},
|
||||
|
||||
// not in MPW.
|
||||
{"true", builtin_true},
|
||||
{"false", builtin_false},
|
||||
};
|
||||
|
||||
|
||||
@ -278,9 +319,6 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
|
||||
//std::string expand_vars(const std::string &s, const class Environment &);
|
||||
|
||||
command::~command()
|
||||
{}
|
||||
|
||||
@ -293,7 +331,7 @@ command::~command()
|
||||
|
||||
|
||||
template<class F>
|
||||
int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
||||
int exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||
|
||||
bool echo = true;
|
||||
int rv = 0;
|
||||
@ -302,7 +340,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
||||
|
||||
try {
|
||||
process p;
|
||||
command = expand_vars(command, env);
|
||||
command = expand_vars(command, env, fds);
|
||||
auto tokens = tokenize(command, false);
|
||||
if (tokens.empty()) return 0;
|
||||
parse_tokens(std::move(tokens), p);
|
||||
@ -330,7 +368,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
||||
int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
|
||||
return exec(text, env, throwup, [&](process &p){
|
||||
return exec(text, env, fds, throwup, [&](process &p){
|
||||
|
||||
fdmask newfds = p.fds | fds;
|
||||
|
||||
@ -344,8 +382,6 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (env.test()) return 0;
|
||||
|
||||
if (env.startup()) {
|
||||
fprintf(stderr, "### MPW Shell - startup file may not contain external commands.\n");
|
||||
return 0;
|
||||
@ -359,6 +395,23 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
//fprintf(stderr, "### MPW Shell - Command \"%s\" was not found.\n", name.c_str());
|
||||
//return -1;
|
||||
}
|
||||
|
||||
if (is_script(path)) {
|
||||
// scripts run with an isolated environment.
|
||||
Environment new_env = env.subshell_environment();
|
||||
new_env.set("command", path);
|
||||
new_env.set_argv(p.arguments);
|
||||
|
||||
try {
|
||||
return read_file(new_env, path, newfds);
|
||||
} catch (const exit_command_t &ex) {
|
||||
return ex.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (env.test()) return 0;
|
||||
|
||||
env.set("command", path);
|
||||
p.arguments[0] = path;
|
||||
|
||||
@ -367,7 +420,7 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
}
|
||||
|
||||
template<class F>
|
||||
int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
int eval_exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx){
|
||||
|
||||
std::string name;
|
||||
bool echo = true;
|
||||
@ -376,7 +429,7 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
try {
|
||||
command = expand_vars(command, env);
|
||||
command = expand_vars(command, env, fds);
|
||||
auto tokens = tokenize(command, true);
|
||||
|
||||
if (tokens.empty()) return 0;
|
||||
@ -386,6 +439,10 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
|
||||
rv = fx(tokens);
|
||||
}
|
||||
catch (const exit_command_t &ex) {
|
||||
// convert to execution of input terminated.
|
||||
throw execution_of_input_terminated(ex.value);
|
||||
}
|
||||
|
||||
catch (mpw_error &e) {
|
||||
if (echo) env.echo("%s", command.c_str());
|
||||
@ -406,29 +463,8 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
||||
|
||||
int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
#if 0
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
std::string s = expand_vars(text, env);
|
||||
|
||||
env.set("command", "evaluate");
|
||||
|
||||
env.echo("%s", s.c_str());
|
||||
|
||||
try {
|
||||
auto tokens = tokenize(s, true);
|
||||
if (tokens.empty()) return 0;
|
||||
|
||||
int status = builtin_evaluate(env, std::move(tokens), fds);
|
||||
|
||||
return env.status(status, throwup);
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return env.status(1, throwup);
|
||||
}
|
||||
#endif
|
||||
|
||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "evaluate");
|
||||
return builtin_evaluate(env, std::move(tokens), fds);
|
||||
});
|
||||
@ -439,7 +475,7 @@ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup)
|
||||
|
||||
int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "break");
|
||||
if (!env.loop()) throw break_error();
|
||||
int status = evaluate(BREAK, std::move(tokens), env);
|
||||
@ -447,30 +483,11 @@ int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
return status;
|
||||
});
|
||||
|
||||
#if 0
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
std::string s = expand_vars(text, env);
|
||||
|
||||
env.set("command", "break");
|
||||
|
||||
env.echo("%s", s.c_str());
|
||||
if (!env.loop()) {
|
||||
fputs("### MPW Shell - Break must be within for or loop.\n", stderr);
|
||||
return env.status(-3, throwup);
|
||||
}
|
||||
|
||||
// check/evaluate if clause.
|
||||
int status = evaluate(BREAK, s, env);
|
||||
if (status > 0)
|
||||
throw break_command_t();
|
||||
return env.status(status, throwup);
|
||||
#endif
|
||||
}
|
||||
|
||||
int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "continue");
|
||||
if (!env.loop()) throw continue_error();
|
||||
int status = evaluate(CONTINUE, std::move(tokens), env);
|
||||
@ -478,26 +495,39 @@ int continue_command::execute(Environment &env, const fdmask &fds, bool throwup)
|
||||
return status;
|
||||
});
|
||||
|
||||
#if 0
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
std::string s = expand_vars(text, env);
|
||||
|
||||
env.set("command", "continue");
|
||||
|
||||
env.echo("%s", s.c_str());
|
||||
if (!env.loop()) {
|
||||
fputs("### MPW Shell - Continue must be within for or loop.\n", stderr);
|
||||
return env.status(-3, throwup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check/evaluate if clause.
|
||||
int status = evaluate(CONTINUE, s, env);
|
||||
if (status > 0)
|
||||
throw continue_command_t();
|
||||
return env.status(status, throwup);
|
||||
#endif
|
||||
int exit_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
// exit
|
||||
// exit [number] [if expr ]]
|
||||
// todo --
|
||||
|
||||
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||
env.set("command", "exit");
|
||||
env.status(0);
|
||||
|
||||
if (tokens.size() <= 1) {
|
||||
throw exit_command_t{};
|
||||
}
|
||||
|
||||
|
||||
int status = 0;
|
||||
value v(tokens[1]);
|
||||
// remove the return value to make processing easier.
|
||||
if (v.is_number()) {
|
||||
tokens.erase(tokens.begin() + 1);
|
||||
}
|
||||
status = evaluate(EXIT, std::move(tokens), env);
|
||||
|
||||
if (status) {
|
||||
int ok = v.to_number(0);
|
||||
throw exit_command_t{ok};
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
@ -550,8 +580,9 @@ int pipe_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
rv = children[1]->execute(e, pipe_fd | fds, throwup);
|
||||
|
||||
return e.status(rv, throwup);
|
||||
}
|
||||
|
||||
if (children[0]) return children[0]->execute(e, fds, throwup);
|
||||
if (children[1]) return children[1]->execute(e, fds, throwup);
|
||||
return e.status(0, throwup);
|
||||
@ -576,7 +607,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
return e.status(-3);
|
||||
}
|
||||
|
||||
std::string s = expand_vars(text, e);
|
||||
std::string s = expand_vars(text, e, fds);
|
||||
|
||||
e.echo("%s", s.c_str());
|
||||
|
||||
@ -600,7 +631,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||
}
|
||||
|
||||
template<class F>
|
||||
int begin_end_exec(std::string begin, std::string end, Environment &env, bool throwup, F &&fx) {
|
||||
int begin_end_exec(std::string begin, std::string end, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||
|
||||
|
||||
bool echo = true;
|
||||
@ -610,8 +641,8 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th
|
||||
|
||||
try {
|
||||
process p;
|
||||
begin = expand_vars(begin, env);
|
||||
end = expand_vars(end, env);
|
||||
begin = expand_vars(begin, env, fds);
|
||||
end = expand_vars(end, env, fds);
|
||||
|
||||
auto b = tokenize(begin, true);
|
||||
auto e = tokenize(end, false);
|
||||
@ -638,12 +669,11 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th
|
||||
}
|
||||
|
||||
return env.status(rv, throwup);
|
||||
|
||||
}
|
||||
|
||||
int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
||||
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||
|
||||
env.set("command", type == BEGIN ? "end" : ")");
|
||||
if (b.size() != 1) {
|
||||
@ -663,53 +693,12 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return rv;
|
||||
});
|
||||
|
||||
#if 0
|
||||
|
||||
std::string b = expand_vars(begin, env);
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
|
||||
env.set("command", type == BEGIN ? "end" : ")");
|
||||
|
||||
// echo!
|
||||
env.echo("%s ... %s",
|
||||
b.c_str(),
|
||||
e.c_str()
|
||||
);
|
||||
|
||||
|
||||
|
||||
// tokenize the begin and end commands.
|
||||
// begin may not have extra arguments. end may have redirection.
|
||||
|
||||
auto bt = tokenize(b, true);
|
||||
if (bt.size() != 1) {
|
||||
fprintf(stderr, "### Begin - Too many parameters were specified.\n");
|
||||
fprintf(stderr, "Usage - Begin\n");
|
||||
return env.status(-3);
|
||||
}
|
||||
|
||||
fdmask newfds;
|
||||
int status = check_ends(e, newfds);
|
||||
newfds |= fds;
|
||||
if (status) return env.status(status);
|
||||
|
||||
int rv;
|
||||
env.indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
|
||||
env.echo("%s", type == BEGIN ? "end" : ")");
|
||||
|
||||
return env.status(rv);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
||||
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||
|
||||
env.set("command", "end");
|
||||
if (b.size() != 1) {
|
||||
@ -727,7 +716,7 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
try {
|
||||
env.indent_and([&]{
|
||||
env.loop_indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
}
|
||||
@ -741,58 +730,11 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return rv;
|
||||
});
|
||||
|
||||
|
||||
#if 0
|
||||
std::string b = expand_vars(begin, env);
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
// echo!
|
||||
env.echo("%s ... %s",
|
||||
b.c_str(),
|
||||
e.c_str()
|
||||
);
|
||||
|
||||
// check for extra tokens...
|
||||
auto bt = tokenize(b, true);
|
||||
if (bt.size() != 1) {
|
||||
fprintf(stderr, "### Loop - Too many parameters were specified.\n");
|
||||
fprintf(stderr, "Usage - Loop\n");
|
||||
return env.status(-3);
|
||||
}
|
||||
|
||||
fdmask newfds;
|
||||
int status = check_ends(e, newfds);
|
||||
newfds |= fds;
|
||||
if (status) return env.status(status);
|
||||
|
||||
int rv = 0;
|
||||
for(;;) {
|
||||
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
try {
|
||||
env.loop_indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
}
|
||||
catch (break_command_t &ex) {
|
||||
env.echo("end");
|
||||
break;
|
||||
}
|
||||
catch (continue_command_t &ex) {}
|
||||
env.echo("end");
|
||||
}
|
||||
return env.status(rv);
|
||||
#endif
|
||||
}
|
||||
|
||||
int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
||||
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
@ -826,56 +768,6 @@ int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
|
||||
return rv;
|
||||
});
|
||||
|
||||
#if 0
|
||||
std::string b = expand_vars(begin, env);
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
// echo!
|
||||
env.echo("%s ... %s",
|
||||
b.c_str(),
|
||||
e.c_str()
|
||||
);
|
||||
|
||||
// check for extra tokens...
|
||||
auto bt = tokenize(b, true);
|
||||
if (bt.size() < 3 || strcasecmp(bt[2].string.c_str(), "in")) {
|
||||
fprintf(stderr, "### For - Missing in keyword.\n");
|
||||
fprintf(stderr, "Usage - For name in [word...]\n");
|
||||
return env.status(-3);
|
||||
}
|
||||
|
||||
fdmask newfds;
|
||||
int status = check_ends(e, newfds);
|
||||
newfds |= fds;
|
||||
if (status) return env.status(status);
|
||||
|
||||
int rv = 0;
|
||||
for (int i = 3; i < bt.size(); ++i ) {
|
||||
|
||||
if (control_c) throw execution_of_input_terminated();
|
||||
|
||||
env.set(bt[1].string, bt[i].string);
|
||||
|
||||
try {
|
||||
env.loop_indent_and([&]{
|
||||
rv = vector_command::execute(env, newfds);
|
||||
});
|
||||
}
|
||||
catch (break_command_t &ex) {
|
||||
env.echo("end");
|
||||
break;
|
||||
}
|
||||
catch (continue_command_t &ex) {}
|
||||
env.echo("end");
|
||||
}
|
||||
|
||||
return env.status(rv);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -900,7 +792,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
int tmp;
|
||||
if (first) {
|
||||
first = false;
|
||||
tmp = begin_end_exec(c->clause, end, env, false, [&](token_vector &b, process &p){
|
||||
tmp = begin_end_exec(c->clause, end, env, fds, false, [&](token_vector &b, process &p){
|
||||
|
||||
newfds = p.fds | fds;
|
||||
|
||||
@ -922,7 +814,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
}
|
||||
else {
|
||||
// second...
|
||||
tmp = eval_exec(c->clause, env, false, [&](token_vector &b){
|
||||
tmp = eval_exec(c->clause, env, fds, false, [&](token_vector &b){
|
||||
if (skip || error) return 0;
|
||||
|
||||
int status = evaluate(c->type, std::move(b), env);
|
||||
@ -945,49 +837,5 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||
env.echo("end");
|
||||
if (error) return env.status(error, throwup);
|
||||
return env.status(rv, throwup);
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
|
||||
|
||||
int rv = 0;
|
||||
bool ok = false;
|
||||
|
||||
std::string e = expand_vars(end, env);
|
||||
|
||||
env.set("command", "end");
|
||||
|
||||
// parse end for indirection.
|
||||
fdmask newfds;
|
||||
int status = check_ends(e, newfds);
|
||||
newfds |= fds;
|
||||
if (status) {
|
||||
rv = status;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
for (auto &c : clauses) {
|
||||
std::string s = expand_vars(c->clause, env);
|
||||
|
||||
if (c->type == IF)
|
||||
env.echo("%s ... %s", s.c_str(), e.c_str());
|
||||
else
|
||||
env.echo("%s", s.c_str());
|
||||
|
||||
if (ok) continue;
|
||||
|
||||
int tmp = evaluate(c->type, s, env);
|
||||
if (tmp < 0) { ok = true; rv = tmp; continue; }
|
||||
if (tmp == 0) continue;
|
||||
|
||||
env.indent_and([&](){
|
||||
rv = c->execute(env, newfds);
|
||||
});
|
||||
}
|
||||
|
||||
env.echo("end");
|
||||
return env.status(rv);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
13
command.h
13
command.h
@ -6,7 +6,7 @@
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "phase2-parser.h"
|
||||
#include "phase3.h"
|
||||
|
||||
typedef std::unique_ptr<struct command> command_ptr;
|
||||
typedef std::vector<command_ptr> command_ptr_vector;
|
||||
@ -22,7 +22,7 @@ struct command {
|
||||
{}
|
||||
|
||||
virtual bool terminal() const noexcept {
|
||||
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR;
|
||||
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR || type == EXIT;
|
||||
}
|
||||
|
||||
int type = 0;
|
||||
@ -81,6 +81,15 @@ struct continue_command : public command {
|
||||
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||
};
|
||||
|
||||
struct exit_command : public command {
|
||||
|
||||
template<class S>
|
||||
exit_command(S &&s) : command(EXIT), text(std::forward<S>(s))
|
||||
{}
|
||||
|
||||
std::string text;
|
||||
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||
};
|
||||
|
||||
|
||||
struct binary_command : public command {
|
||||
|
7
config.h.in
Normal file
7
config.h.in
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef __mpw_shell_config_h__
|
||||
#define __mpw_shell_config_h__
|
||||
|
||||
#cmakedefine HAVE_DPRINTF
|
||||
|
||||
#endif
|
||||
|
@ -269,10 +269,10 @@ namespace filesystem {
|
||||
path current_path(error_code& ec) {
|
||||
|
||||
char *cp;
|
||||
char buffer[PATH_MAX];
|
||||
char buffer[PATH_MAX+1];
|
||||
|
||||
ec.clear();
|
||||
cp = ::getcwd(buffer, sizeof(buffer));
|
||||
cp = ::getcwd(buffer, PATH_MAX);
|
||||
if (cp) return path(cp);
|
||||
|
||||
ec = error_code(errno, std::system_category());
|
||||
@ -304,7 +304,7 @@ namespace filesystem {
|
||||
|
||||
path canonical(const path& p, error_code& ec) {
|
||||
char *cp;
|
||||
char buffer[PATH_MAX];
|
||||
char buffer[PATH_MAX+1];
|
||||
|
||||
ec.clear();
|
||||
cp = realpath(p.c_str(), buffer);
|
||||
@ -316,7 +316,7 @@ namespace filesystem {
|
||||
path canonical(const path& p, const path& base, error_code& ec) {
|
||||
|
||||
char *cp;
|
||||
char buffer[PATH_MAX];
|
||||
char buffer[PATH_MAX+1];
|
||||
|
||||
ec.clear();
|
||||
|
||||
|
@ -23,17 +23,12 @@ namespace {
|
||||
FX _fx;
|
||||
};
|
||||
|
||||
void throw_error(int error) {
|
||||
throw std::system_error(error, std::system_category());
|
||||
|
||||
void set_or_throw_error(std::error_code *ec, int error, const std::string &what) {
|
||||
if (ec) *ec = std::error_code(error, std::system_category());
|
||||
else throw std::system_error(error, std::system_category(), what);
|
||||
}
|
||||
|
||||
|
||||
void throw_error(int error, const std::string &what) {
|
||||
throw std::system_error(error, std::system_category(), what);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -41,12 +36,8 @@ namespace {
|
||||
|
||||
namespace {
|
||||
|
||||
void throw_error() {
|
||||
throw_error(GetLastError());
|
||||
}
|
||||
|
||||
void throw_error(const std::string &what) {
|
||||
throw_error(GetLastError(), what);
|
||||
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||
set_or_throw_error(ec, GetLastError(), what);
|
||||
}
|
||||
|
||||
}
|
||||
@ -61,7 +52,7 @@ void mapped_file_base::close() {
|
||||
}
|
||||
}
|
||||
|
||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
|
||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||||
|
||||
|
||||
HANDLE fh;
|
||||
@ -70,9 +61,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
// length of 0 in CreateFileMapping / MapViewOfFile
|
||||
// means map the entire file.
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
||||
}
|
||||
if (is_open()) close();
|
||||
|
||||
fh = CreateFile(p.c_str(),
|
||||
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||
@ -83,7 +72,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
nullptr
|
||||
);
|
||||
if (fh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
return set_or_throw_error(ec, "CreateFile");
|
||||
}
|
||||
|
||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||
@ -116,7 +105,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
|
||||
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||
if (mh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
return set_or_throw_error(ec, "CreateFileMapping");
|
||||
}
|
||||
|
||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||
@ -129,7 +118,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
length,
|
||||
nullptr);
|
||||
if (!_data) {
|
||||
throw_error();
|
||||
return set_or_throw_error(ec, "MapViewOfFileEx");
|
||||
}
|
||||
|
||||
|
||||
@ -152,10 +141,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
const DWORD access = FILE_MAP_WRITE;
|
||||
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
||||
}
|
||||
|
||||
if (is_open()) close();
|
||||
|
||||
fh = CreateFile(p.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
@ -166,7 +152,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
nullptr
|
||||
);
|
||||
if (fh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
return set_or_throw_error(nullptr, "CreateFile");
|
||||
}
|
||||
|
||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||
@ -174,11 +160,11 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
|
||||
file_size.QuadPart = length;
|
||||
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN));
|
||||
if (!SetEndOfFile(fh)) throw_error();
|
||||
if (!SetEndOfFile(fh)) return set_or_throw_error(nullptr, "SetEndOfFile");
|
||||
|
||||
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||
if (mh == INVALID_HANDLE_VALUE) {
|
||||
throw_error();
|
||||
return set_or_throw_error(nullptr, "CreateFileMapping");
|
||||
}
|
||||
|
||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||
@ -191,7 +177,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
nullptr);
|
||||
|
||||
if (!_data) {
|
||||
throw_error();
|
||||
return set_or_throw_error(nullptr, "MapViewOfFileEx");
|
||||
}
|
||||
|
||||
_file_handle = fh_close.release();
|
||||
@ -213,13 +199,10 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
|
||||
namespace {
|
||||
|
||||
void throw_error() {
|
||||
throw_error(errno);
|
||||
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||
set_or_throw_error(ec, errno, what);
|
||||
}
|
||||
|
||||
void throw_error(const std::string &what) {
|
||||
throw_error(errno, what);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -232,15 +215,15 @@ void mapped_file_base::close() {
|
||||
}
|
||||
|
||||
|
||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
|
||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||||
|
||||
if (ec) ec->clear();
|
||||
|
||||
int fd;
|
||||
|
||||
int oflags = 0;
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
||||
}
|
||||
if (is_open()) close();
|
||||
|
||||
switch (flags) {
|
||||
case readonly:
|
||||
@ -253,7 +236,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
|
||||
fd = ::open(p.c_str(), oflags);
|
||||
if (fd < 0) {
|
||||
throw_error(errno);
|
||||
return set_or_throw_error(ec, "open");
|
||||
}
|
||||
|
||||
//defer([fd](){::close(fd); });
|
||||
@ -265,7 +248,8 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
struct stat st;
|
||||
|
||||
if (::fstat(fd, &st) < 0) {
|
||||
throw_error(errno);
|
||||
set_or_throw_error(ec, "stat");
|
||||
return;
|
||||
}
|
||||
length = st.st_size;
|
||||
}
|
||||
@ -279,7 +263,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||
|
||||
if (_data == MAP_FAILED) {
|
||||
_data = nullptr;
|
||||
throw_error(errno);
|
||||
return set_or_throw_error(ec, "mmap");
|
||||
}
|
||||
|
||||
_fd = close_fd.release();
|
||||
@ -292,15 +276,11 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
int fd;
|
||||
const size_t offset = 0;
|
||||
|
||||
if (is_open()) {
|
||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
||||
}
|
||||
if (is_open()) close();
|
||||
|
||||
|
||||
|
||||
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
|
||||
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
if (fd < 0) {
|
||||
throw_error(errno);
|
||||
return set_or_throw_error(nullptr, "open");
|
||||
}
|
||||
|
||||
//defer([fd](){::close(fd); });
|
||||
@ -309,7 +289,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
|
||||
|
||||
if (::ftruncate(fd, length) < 0) {
|
||||
throw_error(errno);
|
||||
return set_or_throw_error(nullptr, "ftruncate");
|
||||
}
|
||||
|
||||
|
||||
@ -320,7 +300,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||
|
||||
if (_data == MAP_FAILED) {
|
||||
_data = nullptr;
|
||||
throw_error(errno);
|
||||
return set_or_throw_error(nullptr, "mmap");
|
||||
}
|
||||
|
||||
_fd = close_fd.release();
|
||||
|
@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
class mapped_file_base {
|
||||
public:
|
||||
@ -40,7 +40,7 @@ protected:
|
||||
|
||||
void swap(mapped_file_base &rhs);
|
||||
|
||||
void open(const path_type& p, mapmode flags, size_t length, size_t offset);
|
||||
void open(const path_type& p, mapmode flags, size_t length, size_t offset, std::error_code *ec);
|
||||
void create(const path_type &p, size_t new_size); // always creates readwrite.
|
||||
void reset();
|
||||
|
||||
@ -79,6 +79,22 @@ public:
|
||||
open(p, flags, length, offset);
|
||||
}
|
||||
|
||||
mapped_file(const path_type &p, std::error_code &ec) noexcept {
|
||||
open(p, readonly, -1, 0, ec);
|
||||
}
|
||||
mapped_file(const path_type &p, mapmode flags, std::error_code &ec) noexcept {
|
||||
open(p, flags, -1, 0, ec);
|
||||
}
|
||||
|
||||
mapped_file(const path_type &p, mapmode flags, size_t length, std::error_code &ec) noexcept {
|
||||
open(p, flags, length, 0, ec);
|
||||
}
|
||||
|
||||
mapped_file(const path_type &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept {
|
||||
open(p, flags, length, offset, ec);
|
||||
}
|
||||
|
||||
|
||||
mapped_file(mapped_file &&);
|
||||
mapped_file(const mapped_file &) = delete;
|
||||
|
||||
@ -87,7 +103,20 @@ public:
|
||||
|
||||
|
||||
void open(const path_type& p, mapmode flags, size_t length = -1, size_t offset = 0) {
|
||||
base::open(p, flags, length, offset);
|
||||
base::open(p, flags, length, offset, nullptr);
|
||||
}
|
||||
|
||||
void open(const path_type &p, std::error_code &ec) noexcept {
|
||||
base::open(p, readonly, -1, 0, &ec);
|
||||
}
|
||||
void open(const path_type &p, mapmode flags, std::error_code &ec) noexcept {
|
||||
base::open(p, flags, -1, 0, &ec);
|
||||
}
|
||||
void open(const path_type &p, mapmode flags, size_t length, std::error_code &ec) noexcept {
|
||||
base::open(p, flags, length, 0, &ec);
|
||||
}
|
||||
void open(const path_type &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept {
|
||||
base::open(p, flags, length, offset, &ec);
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,6 +23,10 @@ namespace {
|
||||
|
||||
bool tf(long v) { return v; }
|
||||
|
||||
bool tf(const EnvironmentEntry &e) {
|
||||
return tf(static_cast<std::string>(e));
|
||||
}
|
||||
|
||||
// used for #. base 10 only, extra chars ignored.
|
||||
int to_pound_int(const std::string &s) {
|
||||
if (s.empty()) return 0;
|
||||
@ -38,6 +42,29 @@ namespace {
|
||||
|
||||
}
|
||||
|
||||
|
||||
Environment Environment::subshell_environment() {
|
||||
/* clone the current environment, do not include local variables */
|
||||
Environment env;
|
||||
env._alias_table = _alias_table;
|
||||
|
||||
auto &table = env._table;
|
||||
for (const auto &kv : _table) {
|
||||
const auto &k = kv.first;
|
||||
const auto &value = kv.second;
|
||||
if (!value) continue;
|
||||
|
||||
if (k == "echo") env._echo = tf(value);
|
||||
if (k == "exit") env._exit = tf(value);
|
||||
if (k == "test") env._test = tf(value);
|
||||
|
||||
table.emplace_hint(table.end(), k, value);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
|
||||
std::string Environment::get(const std::string & key) const {
|
||||
auto iter = find(key);
|
||||
if (iter == end()) return "";
|
||||
@ -85,15 +112,17 @@ namespace {
|
||||
set_common(k, std::to_string(value), exported);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void Environment::set_argv(const std::string &argv0, const std::vector<std::string>& argv) {
|
||||
set_common("0", argv0, false);
|
||||
set_argv(argv);
|
||||
}
|
||||
#endif
|
||||
void Environment::set_argv(const std::vector<std::string>& argv) {
|
||||
_pound = argv.size();
|
||||
set_common("#", std::to_string(argv.size()), false);
|
||||
_pound = argv.size() - 1;
|
||||
set_common("#", std::to_string(_pound), false);
|
||||
|
||||
int n = 1;
|
||||
int n = 0;
|
||||
for (const auto &s : argv) {
|
||||
set_common(std::to_string(n++), s, false);
|
||||
}
|
||||
@ -116,8 +145,22 @@ namespace {
|
||||
}
|
||||
p.pop_back();
|
||||
set_common("parameters", p, false);
|
||||
}
|
||||
|
||||
void Environment::shift(int n) {
|
||||
if (n < 0) return;
|
||||
if (_pound < 1) return;
|
||||
|
||||
std::vector<std::string> argv;
|
||||
argv.push_back(get("0"));
|
||||
|
||||
for (int i = 1 + n; i <= _pound; ++i) {
|
||||
argv.push_back(get(std::to_string(i)));
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
unset(std::to_string(_pound - i));
|
||||
}
|
||||
set_argv(argv);
|
||||
}
|
||||
|
||||
|
||||
@ -185,8 +228,8 @@ namespace {
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
va_end(ap);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
}
|
||||
|
@ -56,11 +56,14 @@ public:
|
||||
typedef std::vector<std::pair<std::string, std::string>> alias_table_type;
|
||||
typedef alias_table_type::const_iterator const_alias_iterator;
|
||||
|
||||
//const EnvironmentEntry & lookup(const std::string &s);
|
||||
|
||||
void set_argv(const std::string &argv0, const std::vector<std::string>& argv);
|
||||
Environment subshell_environment();
|
||||
|
||||
// void set_argv(const std::string &argv0, const std::vector<std::string>& argv);
|
||||
void set_argv(const std::vector<std::string>& argv);
|
||||
|
||||
void shift(int n);
|
||||
|
||||
void set(const std::string &k, const std::string &value, bool exported = false);
|
||||
void set(const std::string &k, long l, bool exported = false);
|
||||
void unset(const std::string &k);
|
||||
@ -80,10 +83,6 @@ public:
|
||||
bool startup() const noexcept { return _startup; }
|
||||
void startup(bool tf) noexcept { _startup = tf; }
|
||||
|
||||
|
||||
bool passthrough() const noexcept { return _passthrough; }
|
||||
void passthrough(bool tf) noexcept { _passthrough = tf; }
|
||||
|
||||
template<class FX>
|
||||
void foreach(FX && fx) { for (const auto &kv : _table) { fx(kv.first, kv.second); }}
|
||||
|
||||
@ -133,8 +132,6 @@ public:
|
||||
private:
|
||||
// magic variables.
|
||||
|
||||
friend class indent_helper;
|
||||
|
||||
int _indent = 0;
|
||||
int _loop = 0;
|
||||
|
||||
@ -145,7 +142,6 @@ private:
|
||||
int _status = 0;
|
||||
int _pound = 0;
|
||||
bool _startup = false;
|
||||
bool _passthrough = false;
|
||||
|
||||
void set_common(const std::string &, const std::string &, bool);
|
||||
void rebuild_aliases();
|
||||
@ -155,16 +151,5 @@ private:
|
||||
alias_table_type _alias_table;
|
||||
};
|
||||
|
||||
/*
|
||||
class indent_helper {
|
||||
public:
|
||||
indent_helper(Environment &e) : env(e) { env._indent++; }
|
||||
void release() { if (active) { active = false; env._indent--; }}
|
||||
~indent_helper() { if (active) env._indent--; }
|
||||
private:
|
||||
Environment &env;
|
||||
bool active = true;
|
||||
};
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
35
error.h
35
error.h
@ -67,4 +67,37 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
#endif
|
||||
class fsstring_error: public mpw_error {
|
||||
public:
|
||||
fsstring_error(int status = -3) :
|
||||
mpw_error(status, "MPW Shell - /s must occur in pairs.")
|
||||
{}
|
||||
};
|
||||
|
||||
class bsstring_error: public mpw_error {
|
||||
public:
|
||||
bsstring_error(int status = -3) :
|
||||
mpw_error(status, "MPW Shell - \\s must occur in pairs.")
|
||||
{}
|
||||
};
|
||||
|
||||
class regex_error : public mpw_error {
|
||||
public:
|
||||
regex_error(int status = -2) :
|
||||
mpw_error(status, "MPW Shell - File name pattern is incorrect")
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
these are used for flow-control.
|
||||
they do not inherit from std::exception to prevent being caught
|
||||
by normal handlers.
|
||||
*/
|
||||
|
||||
struct break_command_t {};
|
||||
struct continue_command_t {};
|
||||
struct exit_command_t { int value = 0; };
|
||||
struct quit_command_t {};
|
||||
|
||||
#endif
|
||||
|
@ -49,3 +49,9 @@ set -e MWCIncludes "{CIncludes}"
|
||||
set -e ARMCIncludes "{MPW}Interfaces:ARMCIncludes:"
|
||||
|
||||
set -e AsmMatOpts "-t 4"
|
||||
|
||||
# customizations can go in the UserStartup file.
|
||||
if `exists {MPW}UserStartup` != ""
|
||||
execute {MPW}UserStartup
|
||||
end
|
||||
|
||||
|
34
lemon_base.h
Normal file
34
lemon_base.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef __lemon_base_h__
|
||||
#define __lemon_base_h__
|
||||
#include <cstdio>
|
||||
|
||||
template<class TokenType>
|
||||
class lemon_base {
|
||||
public:
|
||||
typedef TokenType token_type;
|
||||
|
||||
virtual ~lemon_base() = default;
|
||||
|
||||
//virtual typename std::enable_if<std::is_move_constructible<TokenType>::value, void>::type
|
||||
virtual void parse(int yymajor, TokenType &&yyminor) = 0;
|
||||
|
||||
virtual void trace(FILE *, const char *) {}
|
||||
|
||||
virtual bool will_accept() const = 0;
|
||||
virtual void reset() {}
|
||||
|
||||
protected:
|
||||
virtual void parse_accept() {}
|
||||
virtual void parse_failure() {}
|
||||
virtual void stack_overflow() {}
|
||||
virtual void syntax_error(int yymajor, TokenType &yyminor) {}
|
||||
lemon_base() {}
|
||||
|
||||
private:
|
||||
lemon_base(const lemon_base &) = delete;
|
||||
lemon_base(lemon_base &&) = delete;
|
||||
lemon_base &operator=(const lemon_base &) = delete;
|
||||
lemon_base &operator=(lemon_base &&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
@ -81,7 +81,7 @@ std::string utf8_to_macroman(const std::string &s) {
|
||||
rv.reserve(s.size());
|
||||
|
||||
unsigned cs = 0;
|
||||
uint16_t tmp;
|
||||
uint16_t tmp = 0;
|
||||
|
||||
for (unsigned char c : s) {
|
||||
switch(cs) {
|
||||
|
@ -28,4 +28,5 @@ ok = system(*%w(cmake --build build))
|
||||
ok = system(*%w(git add version.h))
|
||||
ok = system(*%w(git commit -m), "Bump Version: #{VERSION}")
|
||||
ok = system(*%w(git tag), "r#{VERSION}")
|
||||
exit 0
|
||||
exit 0
|
||||
|
||||
|
269
mpw-regex.cpp
Normal file
269
mpw-regex.cpp
Normal file
@ -0,0 +1,269 @@
|
||||
|
||||
#include "mpw-regex.h"
|
||||
#include "environment.h"
|
||||
|
||||
typedef std::string::const_iterator iterator;
|
||||
|
||||
namespace {
|
||||
bool ecma_special(unsigned char c) {
|
||||
//
|
||||
switch(c) {
|
||||
case '|':
|
||||
case '{':
|
||||
case '}':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '*':
|
||||
case '+':
|
||||
case '^':
|
||||
case '$':
|
||||
case '.':
|
||||
case '\\':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mpw_regex::mpw_regex(const std::string &s, bool slash) {
|
||||
convert_re(s, slash);
|
||||
}
|
||||
|
||||
bool mpw_regex::is_glob(const std::string &s) {
|
||||
bool esc = false;
|
||||
for (unsigned char c : s) {
|
||||
if (esc) {
|
||||
esc = false;
|
||||
continue;
|
||||
}
|
||||
switch(c) {
|
||||
case 0xb6:
|
||||
esc = true;
|
||||
break;
|
||||
case '[':
|
||||
case '?':
|
||||
case '*':
|
||||
case '+':
|
||||
case 0xc7:
|
||||
case 0xc5:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mpw_regex::match(const std::string &s, Environment &e) {
|
||||
std::smatch m;
|
||||
bool ok = std::regex_match(s, m, re);
|
||||
if (!ok) return false;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
int index = capture_map[i];
|
||||
|
||||
if (index && index < m.size() && m[index].matched) {
|
||||
std::string v(m[index].first, m[index].second);
|
||||
std::string k("\xa8");
|
||||
k += (i + '0');
|
||||
e.set(k, std::move(v));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mpw_regex::match(const std::string &s) {
|
||||
return std::regex_match(s, re);
|
||||
}
|
||||
|
||||
|
||||
// convert a mpw-flavor regex to std::regex flavor regex.
|
||||
void mpw_regex::convert_re(const std::string &s, bool slash) {
|
||||
|
||||
|
||||
std::string accumulator;
|
||||
auto iter = s.begin();
|
||||
auto end = s.end();
|
||||
|
||||
if (slash) {
|
||||
if (iter == end || *iter++ != '/')
|
||||
throw std::regex_error(std::regex_constants::error_space);
|
||||
}
|
||||
|
||||
iter = convert_re(iter, end, accumulator, slash ? '/' : 0);
|
||||
|
||||
if (iter != end) throw std::regex_error(std::regex_constants::error_space);
|
||||
|
||||
|
||||
re = std::regex(accumulator);
|
||||
if (slash) key = s;
|
||||
else key = "/" + s + "/";
|
||||
}
|
||||
|
||||
|
||||
iterator mpw_regex::convert_re(iterator iter, iterator end, std::string &accumulator, unsigned char term) {
|
||||
|
||||
while (iter != end) {
|
||||
unsigned char c = *iter++;
|
||||
if (c == 0xb6) {
|
||||
// escape
|
||||
if (iter == end) throw std::regex_error(std::regex_constants::error_escape);
|
||||
c = *iter++;
|
||||
if (ecma_special(c))
|
||||
accumulator += '\\';
|
||||
accumulator += c;
|
||||
continue;
|
||||
}
|
||||
if (term && c == term) {
|
||||
return iter;
|
||||
}
|
||||
if (c == '?') {
|
||||
// match any char
|
||||
accumulator += '.';
|
||||
continue;
|
||||
}
|
||||
if (c == 0xc5) {
|
||||
// match any string
|
||||
accumulator += ".*";
|
||||
continue;
|
||||
}
|
||||
if (c == '[') {
|
||||
// begin a set
|
||||
iter = convert_re_set(iter, end, accumulator);
|
||||
continue;
|
||||
}
|
||||
if (c == '(') {
|
||||
// begin a capture
|
||||
iter = convert_re_capture(iter, end, accumulator);
|
||||
continue;
|
||||
}
|
||||
if (c == 0xc7) {
|
||||
// repeat
|
||||
iter = convert_re_repeat(iter, end, accumulator);
|
||||
continue;
|
||||
}
|
||||
if (c == '+' || c == '*') {
|
||||
// same meaning
|
||||
accumulator += c;
|
||||
continue;
|
||||
}
|
||||
if (ecma_special(c)) {
|
||||
accumulator += '\\';
|
||||
}
|
||||
accumulator += c;
|
||||
}
|
||||
|
||||
if (term) throw std::regex_error(std::regex_constants::error_paren);
|
||||
return iter;
|
||||
}
|
||||
|
||||
iterator mpw_regex::convert_re_repeat(iterator iter, iterator end, std::string &accumulator) {
|
||||
int min = -1;
|
||||
int max = -1;
|
||||
|
||||
accumulator += "{";
|
||||
|
||||
while (iter != end) {
|
||||
unsigned char c = *iter++;
|
||||
if (c == 0xc8) {
|
||||
accumulator += "}";
|
||||
return iter;
|
||||
}
|
||||
if (c != ',' && !isdigit(c)) break;
|
||||
accumulator += c;
|
||||
}
|
||||
throw std::regex_error(std::regex_constants::error_brace);
|
||||
}
|
||||
|
||||
iterator mpw_regex::convert_re_set(iterator iter, iterator end, std::string &accumulator) {
|
||||
// need extra logic to block character classes.
|
||||
|
||||
unsigned char c;
|
||||
accumulator += "[";
|
||||
|
||||
if (iter != end && static_cast<unsigned char>(*iter) == 0xc2) {
|
||||
accumulator += "^";
|
||||
++iter;
|
||||
} else if (iter != end && *iter == '^') {
|
||||
// leading ^ needs to be escaped.
|
||||
accumulator += "\\^";
|
||||
++iter;
|
||||
}
|
||||
while (iter != end) {
|
||||
c = *iter++;
|
||||
|
||||
if (c == 0xb6) {
|
||||
// escape
|
||||
if (iter == end) throw std::regex_error(std::regex_constants::error_escape);
|
||||
c = *iter++;
|
||||
accumulator += '\\';
|
||||
accumulator += c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ']') {
|
||||
accumulator += "]";
|
||||
return iter;
|
||||
}
|
||||
if (c == '\\') {
|
||||
accumulator += "\\\\";
|
||||
continue;
|
||||
}
|
||||
accumulator += c;
|
||||
}
|
||||
|
||||
throw std::regex_error(std::regex_constants::error_brack);
|
||||
}
|
||||
|
||||
iterator mpw_regex::convert_re_capture(iterator iter, iterator end, std::string &accumulator) {
|
||||
|
||||
|
||||
/*
|
||||
* consider: (abc(abc)®1(xyz))®2
|
||||
* m[1] = (abcabcxyz)
|
||||
* m[2] = (abc)
|
||||
* BUT we don't know if it's captured until the ® is parsed.
|
||||
*/
|
||||
|
||||
std::string scratch;
|
||||
bool capture = false;
|
||||
int n = -1;
|
||||
|
||||
int ecma_index = ++num_captures;
|
||||
|
||||
if (iter != end && *iter == '?') {
|
||||
// leading ? needs to be escaped.
|
||||
scratch += "\\?";
|
||||
++iter;
|
||||
}
|
||||
iter = convert_re(iter, end, scratch, ')');
|
||||
|
||||
// check for capture?
|
||||
if (iter != end && static_cast<unsigned char>(*iter) == 0xa8) {
|
||||
++iter;
|
||||
if (iter == end || !isdigit(*iter))
|
||||
throw std::regex_error(std::regex_constants::error_badbrace); // eh
|
||||
n = *iter++ - '0';
|
||||
capture = true;
|
||||
}
|
||||
|
||||
accumulator += '(';
|
||||
if (capture) {
|
||||
/// ummm capture within a capture? backwards?
|
||||
capture_map[n] = ecma_index;
|
||||
} else {
|
||||
accumulator += "?:";
|
||||
// re-number all sub-captures.
|
||||
--num_captures;
|
||||
for (int &index : capture_map) {
|
||||
if (index >= ecma_index) --index;
|
||||
}
|
||||
}
|
||||
accumulator += scratch;
|
||||
accumulator += ')';
|
||||
return iter;
|
||||
}
|
48
mpw-regex.h
Normal file
48
mpw-regex.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef __mpw_regex_h__
|
||||
#define __mpw_regex_h__
|
||||
|
||||
#include "environment.h"
|
||||
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
class mpw_regex {
|
||||
|
||||
public:
|
||||
|
||||
mpw_regex(const std::string &s, bool slash);
|
||||
|
||||
mpw_regex(const mpw_regex &) = default;
|
||||
// mpw_regex(mpw_regex &&) = default;
|
||||
|
||||
~mpw_regex() = default;
|
||||
|
||||
|
||||
mpw_regex &operator=(const mpw_regex &) = default;
|
||||
// mpw_regex &operator=(mpw_regex &&) = default;
|
||||
|
||||
bool match(const std::string &, class Environment &);
|
||||
bool match(const std::string &);
|
||||
|
||||
static bool is_glob(const std::string &s);
|
||||
|
||||
private:
|
||||
typedef std::string::const_iterator iterator;
|
||||
|
||||
|
||||
void convert_re(const std::string &, bool slash);
|
||||
|
||||
iterator convert_re(iterator iter, iterator end, std::string &accumulator, unsigned char term);
|
||||
iterator convert_re_repeat(iterator iter, iterator end, std::string &accumulator);
|
||||
iterator convert_re_set(iterator iter, iterator end, std::string &accumulator);
|
||||
iterator convert_re_capture(iterator iter, iterator end, std::string &accumulator);
|
||||
|
||||
|
||||
std::regex re;
|
||||
std::string key;
|
||||
int capture_map[10] = {}; // map mpw capture number to ecma group
|
||||
int num_captures = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <algorithm>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@ -42,16 +44,29 @@
|
||||
fgoto *xcs;
|
||||
}
|
||||
|
||||
action einit{ /* einit */ ev.clear(); xcs = fcurs; fgoto estring_state; }
|
||||
action epush{ /* epush */ ev.push_back(fc); }
|
||||
action efinish1{
|
||||
action einit { /* einit */ ev.clear(); xcs = fcurs; fgoto estring_state; }
|
||||
action epush { /* epush */ ev.push_back(fc); }
|
||||
action efinish1 {
|
||||
/* efinish1 */
|
||||
|
||||
/*
|
||||
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
||||
*/
|
||||
|
||||
std::string s = subshell(ev, env, fds);
|
||||
scratch.append(s);
|
||||
|
||||
fgoto *xcs;
|
||||
}
|
||||
action efinish2{
|
||||
action efinish2 {
|
||||
/* efinish2 */
|
||||
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
||||
|
||||
std::string s = subshell(ev, env, fds);
|
||||
for (auto c : s) {
|
||||
if (c == '\'' || c == '"' ) scratch.push_back(escape);
|
||||
scratch.push_back(c);
|
||||
}
|
||||
|
||||
fgoto *xcs;
|
||||
}
|
||||
|
||||
@ -65,12 +80,18 @@
|
||||
|
||||
|
||||
escape = 0xb6;
|
||||
char = any - escape - ['"{`];
|
||||
char = any - escape - ['"{`/\\];
|
||||
escape_seq = escape any;
|
||||
|
||||
schar = [^'];
|
||||
sstring = ['] schar** ['];
|
||||
|
||||
fchar = [^/];
|
||||
fstring = [/] fchar** [/];
|
||||
|
||||
bchar = [^\\];
|
||||
bstring = [\\] bchar** [\\];
|
||||
|
||||
|
||||
vchar = [^}] $vpush;
|
||||
vchar1 = [^{}] $vpush;
|
||||
@ -99,6 +120,8 @@
|
||||
main := (
|
||||
escape_seq $push
|
||||
| sstring $push
|
||||
| fstring $push
|
||||
| bstring $push
|
||||
| dstring $push
|
||||
| vstring
|
||||
| estring
|
||||
@ -110,9 +133,56 @@
|
||||
|
||||
namespace {
|
||||
%% write data;
|
||||
|
||||
|
||||
std::string subshell(const std::string &s, Environment &env, const fdmask &fds) {
|
||||
|
||||
|
||||
char temp[32] = "/tmp/mpw-shell-XXXXXXXX";
|
||||
|
||||
int fd = mkstemp(temp);
|
||||
unlink(temp);
|
||||
|
||||
fdset new_fds;
|
||||
new_fds.set(1, fd);
|
||||
|
||||
int rv = 0;
|
||||
env.indent_and([&](){
|
||||
|
||||
rv = read_string(env, s, new_fds | fds);
|
||||
|
||||
});
|
||||
|
||||
std::string tmp;
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
for(;;) {
|
||||
uint8_t buffer[1024];
|
||||
ssize_t len = read(fd, buffer, sizeof(buffer));
|
||||
if (len == 0) break;
|
||||
if (len < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
throw std::system_error(errno, std::system_category(), "read");
|
||||
}
|
||||
tmp.append(buffer, buffer + len);
|
||||
}
|
||||
|
||||
|
||||
/* if present, a trailing carriage return is stripped */
|
||||
|
||||
if (!tmp.empty()) {
|
||||
if (tmp.back() == '\r' || tmp.back() == '\n') tmp.pop_back();
|
||||
}
|
||||
|
||||
std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](uint8_t x){
|
||||
if (x == '\r' || x == '\n') x = ' ';
|
||||
return x;
|
||||
});
|
||||
return tmp;
|
||||
}
|
||||
|
||||
std::string expand_vars(const std::string &s, const Environment &env) {
|
||||
}
|
||||
|
||||
std::string expand_vars(const std::string &s, Environment &env, const fdmask &fds) {
|
||||
if (s.find_first_of("{`", 0, 2) == s.npos) return s;
|
||||
|
||||
int cs;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "fdset.h"
|
||||
#include "value.h"
|
||||
#include "error.h"
|
||||
#include "mpw-regex.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@ -172,8 +173,8 @@ class expression_parser {
|
||||
|
||||
public:
|
||||
|
||||
expression_parser(const std::string &n, std::vector<token> &&t) :
|
||||
name(n), tokens(std::move(t))
|
||||
expression_parser(Environment &e, const std::string &n, std::vector<token> &&t) :
|
||||
environment(e), name(n), tokens(std::move(t))
|
||||
{}
|
||||
|
||||
expression_parser(const expression_parser &) = delete;
|
||||
@ -194,6 +195,7 @@ private:
|
||||
|
||||
|
||||
value eval(int op, value &lhs, value &rhs);
|
||||
value eval_regex(value &lhs, value &rhs);
|
||||
|
||||
[[noreturn]] void expect_binary_operator();
|
||||
[[noreturn]] void end_of_expression();
|
||||
@ -207,6 +209,7 @@ private:
|
||||
if (!tokens.empty()) tokens.pop_back();
|
||||
}
|
||||
|
||||
Environment &environment;
|
||||
const std::string &name;
|
||||
std::vector<token> tokens;
|
||||
};
|
||||
@ -319,12 +322,14 @@ int expression_parser::precedence(int op) {
|
||||
case '<=':
|
||||
case '>':
|
||||
case '>=':
|
||||
case 0xb2:
|
||||
case 0xb3:
|
||||
return 6;
|
||||
|
||||
case '==':
|
||||
case '!=':
|
||||
case token::equivalent:
|
||||
case token::not_equivalent:
|
||||
case '=~':
|
||||
case '!~':
|
||||
return 7;
|
||||
case '&':
|
||||
return 8;
|
||||
@ -341,6 +346,25 @@ int expression_parser::precedence(int op) {
|
||||
//throw std::runtime_error("unimplemented op";);
|
||||
}
|
||||
|
||||
value expression_parser::eval_regex(value &lhs, value &rhs) {
|
||||
try {
|
||||
mpw_regex re(rhs.string, true);
|
||||
// todo -- need environment to store matches.
|
||||
bool ok = re.match(lhs.string, environment);
|
||||
return ok ? 1 : 0;
|
||||
|
||||
} catch (std::exception &ex) {
|
||||
std::string error;
|
||||
error = name;
|
||||
if (rhs.string.empty() || rhs.string.front() != '/')
|
||||
error += " - Missing /s around regular expression: ";
|
||||
else
|
||||
error += " - Invalid regular expression encountered: ";
|
||||
error += rhs.string;
|
||||
throw mpw_error(-5, error);
|
||||
}
|
||||
}
|
||||
|
||||
value expression_parser::eval(int op, value &lhs, value &rhs) {
|
||||
switch (op) {
|
||||
|
||||
@ -366,9 +390,11 @@ value expression_parser::eval(int op, value &lhs, value &rhs) {
|
||||
return lhs.to_number() < rhs.to_number();
|
||||
|
||||
case '<=':
|
||||
case 0xb2:
|
||||
return lhs.to_number() <= rhs.to_number();
|
||||
|
||||
case '>=':
|
||||
case 0xb3:
|
||||
return lhs.to_number() >= rhs.to_number();
|
||||
|
||||
case '>>':
|
||||
@ -407,6 +433,12 @@ value expression_parser::eval(int op, value &lhs, value &rhs) {
|
||||
return lhs.string != rhs.string;
|
||||
|
||||
|
||||
case '=~':
|
||||
return eval_regex(lhs, rhs);
|
||||
|
||||
case '!~':
|
||||
return !eval_regex(lhs, rhs).number;
|
||||
|
||||
}
|
||||
// todo...
|
||||
throw std::runtime_error("unimplemented op");
|
||||
@ -469,8 +501,8 @@ int32_t expression_parser::evaluate() {
|
||||
return v.to_number(1);
|
||||
}
|
||||
|
||||
int32_t evaluate_expression(const std::string &name, std::vector<token> &&tokens) {
|
||||
int32_t evaluate_expression(Environment &env, const std::string &name, std::vector<token> &&tokens) {
|
||||
|
||||
expression_parser p(name, std::move(tokens));
|
||||
expression_parser p(env, name, std::move(tokens));
|
||||
return p.evaluate();
|
||||
}
|
||||
|
@ -37,13 +37,22 @@
|
||||
dchar = escape_seq | (any - escape - ["]);
|
||||
dstring = ["] dchar** ["] $err{ throw dstring_error(); } ;
|
||||
|
||||
# search-forward string
|
||||
# fschar = escape_seq | (any - escape - [/]);
|
||||
fchar = [^/];
|
||||
fstring = [/] fchar** [/] $err{ throw fsstring_error(); } ;
|
||||
|
||||
# search-backward string
|
||||
# bschar = escape_seq | (any - escape - [\\]);
|
||||
bchar = [^\\];
|
||||
bstring = [\\] bchar** [\\] $err{ throw bsstring_error(); } ;
|
||||
|
||||
action eval { eval }
|
||||
|
||||
# > == start state (single char tokens or common prefix)
|
||||
# % == final state (multi char tokens w/ unique prefix)
|
||||
# $ == all states
|
||||
char = any - ['"];
|
||||
char = any - ['"/\\];
|
||||
main := |*
|
||||
ws+ >push_token;
|
||||
'>>' %push_token => { tokens.emplace_back(">>", '>>'); };
|
||||
@ -61,6 +70,22 @@
|
||||
|
||||
# eval-only.
|
||||
|
||||
# macroman ≤
|
||||
0xb2 when eval
|
||||
%push_token => { tokens.emplace_back("\xb2", '<='); };
|
||||
|
||||
# macroman ≠
|
||||
0xad when eval
|
||||
%push_token => { tokens.emplace_back("\xad", '!='); };
|
||||
|
||||
# macroman ¬
|
||||
0xc2 when eval
|
||||
%push_token => { tokens.emplace_back("\xc2", '!'); };
|
||||
|
||||
# macroman ÷
|
||||
0xd6 when eval
|
||||
%push_token => { tokens.emplace_back("\xd6", '/'); };
|
||||
|
||||
'||' when eval
|
||||
%push_token => { tokens.emplace_back("||", '||'); };
|
||||
'|' when eval
|
||||
@ -69,13 +94,14 @@
|
||||
'&&' when eval
|
||||
%push_token => { tokens.emplace_back("&&", '&&'); };
|
||||
|
||||
|
||||
'(' when eval
|
||||
%push_token => { tokens.emplace_back("(", '('); };
|
||||
|
||||
')' when eval
|
||||
%push_token => { tokens.emplace_back(")", ')'); };
|
||||
|
||||
'<>' when eval
|
||||
%push_token => { tokens.emplace_back("<>", '!='); };
|
||||
|
||||
'<<' when eval
|
||||
%push_token => { tokens.emplace_back("<<", '<<'); };
|
||||
@ -106,7 +132,7 @@
|
||||
|
||||
|
||||
'-' when eval
|
||||
%push_token => { tokens.emplace_back("+", '-'); };
|
||||
%push_token => { tokens.emplace_back("-", '-'); };
|
||||
|
||||
'!' when eval
|
||||
%push_token => { tokens.emplace_back("!", '!'); };
|
||||
@ -127,9 +153,16 @@
|
||||
'-=' when eval
|
||||
%push_token => { tokens.emplace_back("-=", '-='); };
|
||||
|
||||
'=~' when eval
|
||||
%push_token => { tokens.emplace_back("=~", '=~'); };
|
||||
|
||||
'!~' when eval
|
||||
%push_token => { tokens.emplace_back("!~", '!~'); };
|
||||
|
||||
sstring => push_string;
|
||||
dstring => push_string;
|
||||
fstring => push_string;
|
||||
bstring => push_string;
|
||||
escape_seq => push_string;
|
||||
|
||||
char => push;
|
||||
@ -188,11 +221,19 @@ void unquote(token &t) {
|
||||
|
||||
action push { scratch.push_back(fc); }
|
||||
escape = 0xb6;
|
||||
char = any - escape - ['"];
|
||||
char = any - escape - ['"/\\];
|
||||
|
||||
schar = [^'] $push;
|
||||
sstring = ['] schar** ['];
|
||||
|
||||
# // and \\ strings retain the delimiter.
|
||||
fchar = [^/];
|
||||
fstring = ([/] fchar** [/]) $push;
|
||||
|
||||
bchar = [^\\];
|
||||
bstring = ([\\] bchar** [\\]) $push;
|
||||
|
||||
|
||||
ecode =
|
||||
'f' ${ scratch.push_back('\f'); }
|
||||
| 'n' ${ scratch.push_back('\n'); }
|
||||
@ -208,6 +249,8 @@ void unquote(token &t) {
|
||||
main := (
|
||||
escape_seq
|
||||
| sstring
|
||||
| fstring
|
||||
| bstring
|
||||
| dstring
|
||||
| char $push
|
||||
)**;
|
||||
|
357
mpw-shell.cpp
357
mpw-shell.cpp
@ -2,6 +2,8 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@ -10,16 +12,15 @@
|
||||
#include <cerrno>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "mpw-shell.h"
|
||||
#include "mpw_parser.h"
|
||||
|
||||
#include "fdset.h"
|
||||
|
||||
#include "macroman.h"
|
||||
|
||||
#include "phase1.h"
|
||||
#include "phase2.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "cxx/mapped_file.h"
|
||||
#include "cxx/filesystem.h"
|
||||
#include "cxx/string_splitter.h"
|
||||
@ -33,7 +34,6 @@
|
||||
#include <pwd.h>
|
||||
#include <sysexits.h>
|
||||
#include <paths.h>
|
||||
#include <atomic>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
@ -41,32 +41,50 @@ namespace fs = filesystem;
|
||||
|
||||
bool utf8 = false;
|
||||
|
||||
fs::path home() {
|
||||
|
||||
const char *cp = getenv("HOME");
|
||||
if (cp && cp) {
|
||||
auto pw = getpwuid(getuid());
|
||||
if (pw) return fs::path(pw->pw_dir);
|
||||
|
||||
}
|
||||
return fs::path();
|
||||
}
|
||||
|
||||
|
||||
fs::path root() {
|
||||
|
||||
static fs::path root;
|
||||
bool init = false;
|
||||
|
||||
if (root.empty()) {
|
||||
const char *cp = getenv("HOME");
|
||||
if (!cp || !*cp) {
|
||||
auto pw = getpwuid(getuid());
|
||||
if (!pw) {
|
||||
fprintf(stderr,"### Unable to determine home directory\n.");
|
||||
exit(EX_NOUSER);
|
||||
static std::array<filesystem::path, 2> locations = { {
|
||||
"/usr/share/mpw/",
|
||||
"/usr/local/share/mpw/"
|
||||
} };
|
||||
|
||||
if (!init) {
|
||||
init = true;
|
||||
std::error_code ec;
|
||||
fs::path p;
|
||||
|
||||
p = home();
|
||||
if (!p.empty()) {
|
||||
p /= "mpw/";
|
||||
if (fs::is_directory(p, ec)) {
|
||||
root = std::move(p);
|
||||
return root;
|
||||
}
|
||||
cp = pw->pw_dir;
|
||||
}
|
||||
root = cp;
|
||||
root /= "mpw/";
|
||||
fs::error_code ec;
|
||||
fs::file_status st = status(root, ec);
|
||||
if (!fs::exists(st)) {
|
||||
fprintf(stderr, "### Warning: %s does not exist.\n", root.c_str());
|
||||
}
|
||||
|
||||
else if (!fs::is_directory(st)) {
|
||||
fprintf(stderr, "### Warning: %s is not a directory.\n", root.c_str());
|
||||
for (fs::path p : locations) {
|
||||
p /= "mpw/";
|
||||
if (fs::is_directory(p, ec)) {
|
||||
root = std::move(p);
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "### Warning: Unable to find mpw directory.\n");
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@ -80,42 +98,73 @@ void init(Environment &env) {
|
||||
}
|
||||
|
||||
|
||||
int read_file(phase1 &p, const std::string &file) {
|
||||
const mapped_file mf(file, mapped_file::readonly);
|
||||
|
||||
p.process(mf.begin(), mf.end(), false);
|
||||
p.finish();
|
||||
return 0;
|
||||
int read_file(Environment &e, const std::string &file, const fdmask &fds) {
|
||||
std::error_code ec;
|
||||
const mapped_file mf(file, mapped_file::readonly, ec);
|
||||
if (ec) {
|
||||
fprintf(stderr, "# Error reading %s: %s\n", file.c_str(), ec.message().c_str());
|
||||
return e.status(-1, false);
|
||||
}
|
||||
|
||||
|
||||
mpw_parser p(e, fds);
|
||||
e.status(0, false);
|
||||
|
||||
try {
|
||||
p.parse(mf.begin(), mf.end());
|
||||
p.finish();
|
||||
} catch(const execution_of_input_terminated &ex) {
|
||||
return ex.status();
|
||||
}
|
||||
return e.status();
|
||||
}
|
||||
|
||||
int read_fd(phase1 &p, int fd) {
|
||||
|
||||
int read_string(Environment &e, const std::string &s, const fdmask &fds) {
|
||||
mpw_parser p(e, fds);
|
||||
e.status(0, false);
|
||||
try {
|
||||
p.parse(s);
|
||||
p.finish();
|
||||
} catch(const execution_of_input_terminated &ex) {
|
||||
return ex.status();
|
||||
}
|
||||
return e.status();
|
||||
|
||||
}
|
||||
|
||||
|
||||
int read_fd(Environment &e, int fd, const fdmask &fds) {
|
||||
|
||||
unsigned char buffer[2048];
|
||||
ssize_t size;
|
||||
|
||||
for (;;) {
|
||||
size = read(fd, buffer, sizeof(buffer));
|
||||
if (size < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("read: ");
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
if (size == 0) p.finish();
|
||||
else p.process(buffer, buffer + size);
|
||||
} catch(std::exception &ex) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
p.reset();
|
||||
}
|
||||
if (size == 0) break;
|
||||
}
|
||||
mpw_parser p(e, fds);
|
||||
e.status(0, false);
|
||||
|
||||
return 0;
|
||||
try {
|
||||
for (;;) {
|
||||
size = read(fd, buffer, sizeof(buffer));
|
||||
if (size < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("read");
|
||||
e.status(-1, false);
|
||||
}
|
||||
if (size == 0) break;
|
||||
p.parse(buffer, buffer + size);
|
||||
}
|
||||
p.finish();
|
||||
} catch(const execution_of_input_terminated &ex) {
|
||||
return ex.status();
|
||||
}
|
||||
return e.status();
|
||||
}
|
||||
|
||||
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds);
|
||||
fs::path which(const Environment &env, const std::string &name);
|
||||
|
||||
int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::string> &argv) {
|
||||
int read_make(Environment &env, const std::vector<std::string> &argv) {
|
||||
|
||||
int out[2];
|
||||
int ok;
|
||||
@ -147,10 +196,9 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
||||
}
|
||||
|
||||
close(out[1]);
|
||||
int rv = read_fd(p1, out[0]);
|
||||
int rv = read_fd(env, out[0]);
|
||||
close(out[0]);
|
||||
p1.finish();
|
||||
p2.finish();
|
||||
|
||||
|
||||
// check for make errors.
|
||||
for(;;) {
|
||||
@ -176,7 +224,7 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
||||
exit(EX_OSERR);
|
||||
}
|
||||
|
||||
return env.status();
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::atomic<int> control_c{0};
|
||||
@ -190,7 +238,7 @@ void control_c_handler(int signal, siginfo_t *sinfo, void *context) {
|
||||
}
|
||||
|
||||
|
||||
int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
||||
int interactive(Environment &env) {
|
||||
|
||||
std::string history_file = root();
|
||||
history_file += ".history";
|
||||
@ -207,18 +255,18 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
||||
|
||||
sigaction(SIGINT, &act, &old_act);
|
||||
|
||||
mpw_parser p(env, true);
|
||||
|
||||
|
||||
for(;;) {
|
||||
const char *prompt = "# ";
|
||||
if (p1.continuation() || p2.continuation()) prompt = "> ";
|
||||
if (p.continuation()) prompt = "> ";
|
||||
char *cp = readline(prompt);
|
||||
if (!cp) {
|
||||
if (control_c) {
|
||||
control_c = 0;
|
||||
fprintf(stdout, "\n");
|
||||
p1.abort();
|
||||
p2.abort();
|
||||
p.abort();
|
||||
env.status(-9, false);
|
||||
continue;
|
||||
}
|
||||
@ -240,29 +288,17 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
||||
s = utf8_to_macroman(s);
|
||||
|
||||
s.push_back('\n');
|
||||
try {
|
||||
p1.process(s);
|
||||
|
||||
} catch(std::exception &ex) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
p1.reset();
|
||||
}
|
||||
p.parse(s);
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
p1.finish();
|
||||
} catch(std::exception &ex) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
p1.reset();
|
||||
}
|
||||
p.finish();
|
||||
|
||||
sigaction(SIGINT, &old_act, nullptr);
|
||||
|
||||
write_history(history_file.c_str());
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -294,6 +330,10 @@ void define(Environment &env, const std::string &s) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* todo: prevent -r and -s (don't generate shell code)
|
||||
*/
|
||||
void make_help(void) {
|
||||
|
||||
#undef _
|
||||
@ -329,59 +369,92 @@ int make(int argc, char **argv) {
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(argc+1);
|
||||
bool __ = false;
|
||||
int c;
|
||||
bool passthrough = false;
|
||||
|
||||
args.emplace_back("make");
|
||||
static struct option longopts[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "verbose", no_argument, nullptr, 'v' },
|
||||
{ "test", no_argument, nullptr, 1 },
|
||||
{ "dry-run", no_argument, nullptr, 2 },
|
||||
{ nullptr, 0, nullptr, 0},
|
||||
};
|
||||
|
||||
for (auto iter = argv; *iter; ++iter) {
|
||||
std::string tmp(*iter);
|
||||
args.push_back(""); // place-holder.
|
||||
|
||||
if (!__) {
|
||||
if (tmp == "--")
|
||||
{ __ = true; continue; }
|
||||
while ((c = getopt_long(argc, argv, "d:ef:i:prstuvwy", longopts, nullptr)) != -1) {
|
||||
std::string flag = "-"; flag.push_back(c);
|
||||
switch(c) {
|
||||
default:
|
||||
make_help();
|
||||
return EX_USAGE;
|
||||
|
||||
if (tmp == "--help")
|
||||
{ make_help(); exit(0); }
|
||||
case 'h':
|
||||
make_help();
|
||||
return 0;
|
||||
|
||||
if (tmp == "--test" || tmp == "--dry-run")
|
||||
{ e.set("test", "1"); continue; }
|
||||
case 1:
|
||||
e.set("test", 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
passthrough = true;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'f':
|
||||
case 'i':
|
||||
args.push_back(std::move(flag));
|
||||
args.push_back(optarg);
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'p':
|
||||
case 't':
|
||||
case 'u':
|
||||
case 'v':
|
||||
case 'w':
|
||||
case 'y':
|
||||
args.push_back(std::move(flag));
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
case 's':
|
||||
args.push_back(std::move(flag));
|
||||
passthrough = true;
|
||||
break;
|
||||
}
|
||||
args.emplace_back(std::move(tmp));
|
||||
|
||||
|
||||
}
|
||||
|
||||
phase1 p1;
|
||||
phase2 p2;
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
std::transform(argv, argv+argc, std::back_inserter(args), [](const char *cp){
|
||||
return std::string(cp);
|
||||
});
|
||||
|
||||
p1 >>= [&p2](std::string &&s) {
|
||||
if (s.empty()) p2.finish();
|
||||
else p2(std::move(s));
|
||||
};
|
||||
|
||||
p2 >>= [&e](command_ptr_vector &&v) {
|
||||
|
||||
for (auto iter = v.begin(); iter != v.end(); ++iter) {
|
||||
auto &ptr = *iter;
|
||||
fdmask fds;
|
||||
try {
|
||||
ptr->execute(e, fds);
|
||||
} catch (execution_of_input_terminated &ex) {
|
||||
control_c = 0;
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
if (e.exit())
|
||||
exit(ex.status());
|
||||
e.status(ex.status(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
e.startup(true);
|
||||
read_file(p1, root() / "Startup");
|
||||
read_file(e, root() / "Startup");
|
||||
e.startup(false);
|
||||
|
||||
auto path = which(e, "Make");
|
||||
if (path.empty()) {
|
||||
fputs("### MPW Shell - Command \"Make\" was not found.\n", stderr);
|
||||
return -1;
|
||||
}
|
||||
e.set("command", path);
|
||||
args[0] = path;
|
||||
|
||||
return read_make(p1, p2, e, args);
|
||||
if (passthrough) {
|
||||
|
||||
launch_mpw(e, args, fdmask());
|
||||
exit(EX_OSERR);
|
||||
}
|
||||
|
||||
return read_make(e, args);
|
||||
|
||||
}
|
||||
|
||||
@ -458,8 +531,11 @@ int main(int argc, char **argv) {
|
||||
mpw_path();
|
||||
|
||||
fs::path self = fs::path(argv[0]).filename();
|
||||
if (self == "mpw-make") return make(argc - 1, argv + 1);
|
||||
if (self == "mpw-shell" && argc > 1 && !strcmp(argv[1],"make")) return make(argc - 2, argv + 2);
|
||||
if (self == "mpw-make") return make(argc, argv);
|
||||
if (self == "mpw-shell" && argc > 1 && !strcmp(argv[1],"make")) {
|
||||
argv[1] = (char *)"mpw-make";
|
||||
return make(argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
Environment e;
|
||||
init(e);
|
||||
@ -499,59 +575,38 @@ int main(int argc, char **argv) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
phase1 p1;
|
||||
phase2 p2;
|
||||
|
||||
p1 >>= [&p2](std::string &&s) {
|
||||
if (s.empty()) p2.finish();
|
||||
else p2(std::move(s));
|
||||
};
|
||||
|
||||
p2 >>= [&e](command_ptr_vector &&v) {
|
||||
|
||||
for (auto iter = v.begin(); iter != v.end(); ++iter) {
|
||||
auto &ptr = *iter;
|
||||
fdmask fds;
|
||||
try {
|
||||
ptr->execute(e, fds);
|
||||
} catch (execution_of_input_terminated &ex) {
|
||||
control_c = 0;
|
||||
if (!(ptr->terminal() && ++iter == v.end())) {
|
||||
fprintf(stderr, "### %s\n", ex.what());
|
||||
}
|
||||
e.status(ex.status(), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!cflag) fprintf(stdout, "MPW Shell " VERSION "\n");
|
||||
if (!fflag) {
|
||||
fs::path startup = root() / "Startup";
|
||||
e.startup(true);
|
||||
mpw_parser p(e);
|
||||
|
||||
try {
|
||||
read_file(p1, startup);
|
||||
read_file(e, startup);
|
||||
} catch (const std::system_error &ex) {
|
||||
fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what());
|
||||
} catch (const quit_command_t &) {
|
||||
}
|
||||
|
||||
e.startup(false);
|
||||
}
|
||||
|
||||
if (cflag) {
|
||||
std::string s(cflag);
|
||||
s.push_back('\n');
|
||||
p1.process(s, true);
|
||||
p2.finish();
|
||||
exit(e.status());
|
||||
try {
|
||||
|
||||
int rv = 0;
|
||||
if (cflag) {
|
||||
rv = read_string(e, cflag);
|
||||
exit(rv);
|
||||
}
|
||||
|
||||
if (isatty(STDIN_FILENO))
|
||||
rv = interactive(e);
|
||||
else
|
||||
rv = read_fd(e, STDIN_FILENO);
|
||||
|
||||
exit(rv);
|
||||
}
|
||||
catch (const quit_command_t &) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (isatty(STDIN_FILENO))
|
||||
interactive(e, p1, p2);
|
||||
else
|
||||
read_fd(p1, STDIN_FILENO);
|
||||
p2.finish();
|
||||
|
||||
exit(e.status());
|
||||
}
|
||||
|
10
mpw-shell.h
10
mpw-shell.h
@ -8,6 +8,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "environment.h"
|
||||
#include "fdset.h"
|
||||
|
||||
const unsigned char escape = 0xb6;
|
||||
|
||||
@ -49,7 +50,7 @@ public:
|
||||
|
||||
|
||||
std::vector<token> tokenize(std::string &s, bool eval = false);
|
||||
std::string expand_vars(const std::string &s, const class Environment &);
|
||||
std::string expand_vars(const std::string &s, class Environment &, const fdmask &fds = fdmask());
|
||||
|
||||
//std::string quote(std::string &&s);
|
||||
std::string quote(const std::string &s);
|
||||
@ -63,8 +64,13 @@ void parse_tokens(std::vector<token> &&tokens, process &p);
|
||||
|
||||
|
||||
|
||||
int32_t evaluate_expression(const std::string &name, std::vector<token> &&tokens);
|
||||
int32_t evaluate_expression(Environment &e, const std::string &name, std::vector<token> &&tokens);
|
||||
|
||||
|
||||
|
||||
int read_file(Environment &e, const std::string &file, const fdmask &fds = fdmask());
|
||||
int read_string(Environment &e, const std::string &s, const fdmask &fds = fdmask());
|
||||
int read_fd(Environment &e, int fd, const fdmask &fds = fdmask());
|
||||
|
||||
|
||||
#endif
|
||||
|
106
mpw_parser.cpp
Normal file
106
mpw_parser.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "mpw_parser.h"
|
||||
#include "phase3_parser.h"
|
||||
|
||||
#include "command.h"
|
||||
#include "error.h"
|
||||
|
||||
mpw_parser::mpw_parser(Environment &e, fdmask fds, bool interactive) : _env(e), _fds(fds), _interactive(interactive)
|
||||
{
|
||||
|
||||
_p3 = phase3::make();
|
||||
_p2.set_next([this](int type, std::string &&s){
|
||||
_p3->parse(type, std::move(s));
|
||||
});
|
||||
|
||||
_p1.set_next([this](std::string &&s){
|
||||
_p2.parse(std::move(s));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
mpw_parser::~mpw_parser() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool mpw_parser::continuation() const {
|
||||
if (_p1.continuation()) return true;
|
||||
if (_p2.continuation()) return true;
|
||||
if (_p3->continuation()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void mpw_parser::finish() {
|
||||
_p1.finish();
|
||||
_p2.finish();
|
||||
_p3->parse(0, "");
|
||||
|
||||
// and now execute the commands...
|
||||
execute();
|
||||
}
|
||||
|
||||
|
||||
void mpw_parser::reset() {
|
||||
_p1.reset();
|
||||
_p2.reset();
|
||||
_p3->reset();
|
||||
_abort = false;
|
||||
}
|
||||
|
||||
void mpw_parser::abort() {
|
||||
_p1.abort();
|
||||
_p2.abort();
|
||||
//_p3->abort();
|
||||
_p3->reset();
|
||||
_abort = true;
|
||||
}
|
||||
|
||||
|
||||
void mpw_parser::parse(const void *begin, const void *end) {
|
||||
if (_abort) return;
|
||||
_p1.parse((const unsigned char *)begin, (const unsigned char *)end);
|
||||
|
||||
// and execute...
|
||||
execute();
|
||||
}
|
||||
|
||||
|
||||
void mpw_parser::execute() {
|
||||
if (_abort) {
|
||||
_p3->command_queue.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto commands = std::move(_p3->command_queue);
|
||||
_p3->command_queue.clear();
|
||||
|
||||
|
||||
std::reverse(commands.begin(), commands.end());
|
||||
|
||||
command_ptr cmd;
|
||||
|
||||
|
||||
try {
|
||||
while (!commands.empty()) {
|
||||
cmd = std::move(commands.back());
|
||||
commands.pop_back();
|
||||
cmd->execute(_env, _fds);
|
||||
}
|
||||
|
||||
} catch (execution_of_input_terminated &ex) {
|
||||
|
||||
_env.status(ex.status(), false);
|
||||
|
||||
if (_interactive) {
|
||||
if (!cmd->terminal() || !commands.empty()) {
|
||||
if (ex.status()) fprintf(stderr, "### %s\n", ex.what());
|
||||
}
|
||||
return;
|
||||
}
|
||||
_abort = true;
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
59
mpw_parser.h
Normal file
59
mpw_parser.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef __mpw_parser__
|
||||
#define __mpw_parser__
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "environment.h"
|
||||
#include "fdset.h"
|
||||
|
||||
#include "phase1.h"
|
||||
#include "phase2.h"
|
||||
|
||||
class mpw_parser {
|
||||
|
||||
public:
|
||||
|
||||
mpw_parser(Environment &e, fdmask fds = fdmask(), bool interactive = false);
|
||||
mpw_parser(Environment &e, bool interactive) : mpw_parser(e, fdmask(), interactive)
|
||||
{}
|
||||
|
||||
~mpw_parser();
|
||||
|
||||
void parse(const void *begin, const void *end);
|
||||
void parse(const std::string &s) {
|
||||
parse(s.data(), s.data() + s.size());
|
||||
}
|
||||
void parse(const void *begin, size_t length) {
|
||||
parse(begin, (const char *)begin + length);
|
||||
}
|
||||
|
||||
void reset();
|
||||
void abort();
|
||||
void finish();
|
||||
|
||||
bool continuation() const;
|
||||
|
||||
private:
|
||||
|
||||
mpw_parser& operator=(const mpw_parser &) = delete;
|
||||
mpw_parser& operator=(mpw_parser &&) = delete;
|
||||
|
||||
mpw_parser(const mpw_parser &) = delete;
|
||||
mpw_parser(mpw_parser &&) = delete;
|
||||
|
||||
void execute();
|
||||
|
||||
Environment &_env;
|
||||
fdmask _fds;
|
||||
bool _interactive = false;
|
||||
bool _abort = false;
|
||||
|
||||
phase1 _p1;
|
||||
phase2 _p2;
|
||||
std::unique_ptr<class phase3> _p3;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -155,7 +155,7 @@ namespace ToolBox
|
||||
|
||||
}%%
|
||||
|
||||
return str;
|
||||
return std::move(str);
|
||||
|
||||
}
|
||||
|
||||
|
44
phase1.cpp
44
phase1.cpp
@ -25,6 +25,13 @@ enum {
|
||||
st_estring2_esc,
|
||||
|
||||
st_estring3,
|
||||
|
||||
st_fstring,
|
||||
st_fstring_esc,
|
||||
|
||||
st_bstring,
|
||||
st_bstring_esc,
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -54,6 +61,8 @@ int phase1::process(unsigned char c, int st) {
|
||||
case st_sstring_esc:
|
||||
case st_estring1_esc:
|
||||
case st_estring2_esc:
|
||||
case st_fstring_esc:
|
||||
case st_bstring_esc:
|
||||
multiline = true;
|
||||
scratch.pop_back();
|
||||
line++;
|
||||
@ -81,6 +90,10 @@ text:
|
||||
return st_sstring;
|
||||
case '`':
|
||||
return st_estring;
|
||||
case '/':
|
||||
return st_fstring;
|
||||
case '\\':
|
||||
return st_bstring;
|
||||
|
||||
default:
|
||||
return st_text;
|
||||
@ -109,6 +122,23 @@ text:
|
||||
return st_sstring;
|
||||
break;
|
||||
|
||||
|
||||
case st_fstring_esc:
|
||||
// fall through
|
||||
case st_fstring:
|
||||
if (c == '/') return st_text;
|
||||
if (c == esc) return st_fstring_esc;
|
||||
return st_fstring;
|
||||
break;
|
||||
|
||||
case st_bstring_esc:
|
||||
// fall through
|
||||
case st_bstring:
|
||||
if (c == '\\') return st_text;
|
||||
if (c == esc) return st_bstring_esc;
|
||||
return st_bstring;
|
||||
break;
|
||||
|
||||
case st_dstring:
|
||||
if (c == '\"') return st_text;
|
||||
if (c == esc) return st_dstring_esc;
|
||||
@ -150,19 +180,10 @@ text:
|
||||
}
|
||||
|
||||
|
||||
void phase1::process(const std::string &s, bool final) {
|
||||
|
||||
for (auto c : s) {
|
||||
cs = process(c, cs);
|
||||
}
|
||||
if (final) finish();
|
||||
}
|
||||
|
||||
void phase1::process(const unsigned char *begin, const unsigned char *end, bool final) {
|
||||
void phase1::parse(const unsigned char *begin, const unsigned char *end) {
|
||||
while (begin != end) {
|
||||
cs = process(*begin++, cs);
|
||||
}
|
||||
if (final) finish();
|
||||
}
|
||||
|
||||
void phase1::finish() {
|
||||
@ -181,7 +202,6 @@ void phase1::reset() {
|
||||
void phase1::flush() {
|
||||
multiline = false;
|
||||
if (scratch.empty()) return;
|
||||
// strip trailing whitespace?
|
||||
if (pipe_to) pipe_to(std::move(scratch));
|
||||
if (_then) _then(std::move(scratch));
|
||||
scratch.clear();
|
||||
}
|
||||
|
32
phase1.h
32
phase1.h
@ -8,47 +8,33 @@
|
||||
class phase1 {
|
||||
|
||||
public:
|
||||
typedef std::function<void(std::string &&)> pipe_function;
|
||||
|
||||
typedef std::function<void(std::string &&)> next_function_type;
|
||||
|
||||
phase1() = default;
|
||||
|
||||
void process(const unsigned char *begin, const unsigned char *end, bool final = false);
|
||||
|
||||
void process(const char *begin, const char *end, bool final = false) {
|
||||
process((const unsigned char *)begin, (const unsigned char *)end, final);
|
||||
}
|
||||
|
||||
void process(const std::string &s, bool final = false);// { process(s.data(), s.data() + s.size(), final); }
|
||||
|
||||
void finish();// { const char *tmp = "\n"; process(tmp, tmp+1, true); }
|
||||
void parse(const unsigned char *begin, const unsigned char *end);
|
||||
void parse(const std::string &s) { parse((const unsigned char *)s.data(), (const unsigned char *)s.data() + s.size()); }
|
||||
void finish();
|
||||
|
||||
void reset();
|
||||
void abort() { reset(); }
|
||||
|
||||
|
||||
phase1 &operator >>= (pipe_function f) { pipe_to = f; return *this; }
|
||||
|
||||
|
||||
template<class F>
|
||||
phase1 &operator >>= (F &f) {
|
||||
using std::placeholders::_1;
|
||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool continuation() const { return multiline; }
|
||||
|
||||
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||
|
||||
private:
|
||||
|
||||
int process(unsigned char, int);
|
||||
void flush();
|
||||
|
||||
std::string scratch;
|
||||
pipe_function pipe_to;
|
||||
int line = 1;
|
||||
int cs = 0;
|
||||
bool multiline = false;
|
||||
|
||||
next_function_type _then;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
72
phase2.h
72
phase2.h
@ -3,95 +3,41 @@
|
||||
#define __phase2_h__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "command.h"
|
||||
#include <lemon_base.h>
|
||||
|
||||
//typedef std::unique_ptr<struct command> command_ptr;
|
||||
//typedef std::vector<command_ptr> command_ptr_vector;
|
||||
|
||||
class phase2_parser : public lemon_base<std::string> {
|
||||
|
||||
public:
|
||||
static std::unique_ptr<phase2_parser> make();
|
||||
|
||||
virtual void syntax_error(int yymajor, std::string &yyminor) override final;
|
||||
virtual void parse_accept() override final;
|
||||
virtual void parse_failure() override final;
|
||||
|
||||
bool continuation() const;
|
||||
|
||||
private:
|
||||
friend class phase2;
|
||||
|
||||
phase2_parser(const phase2_parser &) = delete;
|
||||
phase2_parser(phase2_parser &&) = delete;
|
||||
|
||||
phase2_parser& operator=(const phase2_parser &) = delete;
|
||||
phase2_parser& operator=(phase2_parser &&) = delete;
|
||||
|
||||
protected:
|
||||
// these need to be accessible to the lemon-generated parser.
|
||||
phase2_parser() = default;
|
||||
|
||||
command_ptr_vector command_queue;
|
||||
bool error = false;
|
||||
};
|
||||
|
||||
|
||||
class phase2 {
|
||||
|
||||
public:
|
||||
typedef std::function<void(command_ptr_vector &&)> pipe_function;
|
||||
|
||||
phase2();
|
||||
phase2(const phase2 &) = delete;
|
||||
phase2(phase2 &&) = default;
|
||||
typedef std::function<void(int, std::string &&)> next_function_type;
|
||||
|
||||
phase2 & operator=(const phase2 &) = delete;
|
||||
phase2 & operator=(phase2 &&) = default;
|
||||
phase2() = default;
|
||||
|
||||
void operator()(const std::string &line) { process(line); }
|
||||
void process(const std::string &line);
|
||||
void parse(std::string &&);
|
||||
void finish();
|
||||
|
||||
phase2 &operator >>=(pipe_function f) { pipe_to = f; return *this; }
|
||||
void reset();
|
||||
void abort() { reset(); }
|
||||
|
||||
template<class F>
|
||||
phase2 &operator >>= (F &f) {
|
||||
using std::placeholders::_1;
|
||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
||||
return *this;
|
||||
}
|
||||
bool continuation() const { return false; }
|
||||
|
||||
bool continuation() const {
|
||||
return parser ? parser->continuation() : false;
|
||||
}
|
||||
|
||||
void abort();
|
||||
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||
|
||||
private:
|
||||
|
||||
void parse(int, std::string &&);
|
||||
|
||||
|
||||
std::unique_ptr<phase2_parser> parser;
|
||||
void parse(int type, std::string &&s);
|
||||
|
||||
std::string scratch;
|
||||
int type = 0;
|
||||
int pcount = 0;
|
||||
|
||||
pipe_function pipe_to;
|
||||
|
||||
void flush();
|
||||
bool special();
|
||||
int classify();
|
||||
void exec();
|
||||
|
||||
|
||||
next_function_type _then;
|
||||
};
|
||||
|
||||
|
||||
|
116
phase2.rl
116
phase2.rl
@ -4,9 +4,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "phase2-parser.h"
|
||||
#include "phase2.h"
|
||||
#include "command.h"
|
||||
#include "phase3.h"
|
||||
|
||||
%%{
|
||||
machine main;
|
||||
@ -61,6 +60,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
action parse_lparen {
|
||||
if (scratch.empty()) {
|
||||
parse(LPAREN, "(");
|
||||
@ -72,7 +72,8 @@
|
||||
action parse_rparen {
|
||||
if (pcount <= 0) {
|
||||
flush();
|
||||
parse(RPAREN, ")");
|
||||
// parse(RPAREN, ")");
|
||||
scratch.push_back(fc);
|
||||
fgoto main;
|
||||
}
|
||||
--pcount;
|
||||
@ -87,6 +88,13 @@
|
||||
schar = [^'];
|
||||
sstring = ['] schar** ['] ;
|
||||
|
||||
fchar = [^/];
|
||||
fstring = [/] fchar** [/] ;
|
||||
|
||||
bchar = [^\\];
|
||||
bstring = [\\] bchar** [\\] ;
|
||||
|
||||
|
||||
vchar = [^}];
|
||||
vstring = [{] vchar** [}] ;
|
||||
|
||||
@ -113,6 +121,8 @@
|
||||
| '&' '&' $parse_amp_amp
|
||||
| escape_seq
|
||||
| sstring
|
||||
| fstring
|
||||
| bstring
|
||||
| dstring
|
||||
| vstring
|
||||
| estring
|
||||
@ -171,6 +181,10 @@ int phase2::classify() {
|
||||
%%write data;
|
||||
|
||||
if (type) return type;
|
||||
if (scratch.front() == ')') {
|
||||
type = RPAREN;
|
||||
return type;
|
||||
}
|
||||
std::string argv0;
|
||||
|
||||
const unsigned char *p = (const unsigned char *)scratch.data();
|
||||
@ -194,6 +208,7 @@ int phase2::classify() {
|
||||
_("else", ELSE)
|
||||
_("end", END)
|
||||
_("evaluate", EVALUATE)
|
||||
_("exit", EXIT)
|
||||
_("for", FOR)
|
||||
_("if", IF)
|
||||
_("loop", LOOP)
|
||||
@ -217,9 +232,7 @@ void phase2::flush() {
|
||||
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
||||
|
||||
|
||||
if (!scratch.empty()) {
|
||||
parse(classify(), std::move(scratch));
|
||||
}
|
||||
if (!scratch.empty()) parse(classify(), std::move(scratch));
|
||||
|
||||
type = 0;
|
||||
pcount = 0;
|
||||
@ -236,20 +249,21 @@ bool phase2::special() {
|
||||
case EVALUATE:
|
||||
case BREAK:
|
||||
case CONTINUE:
|
||||
case EXIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void phase2::process(const std::string &line) {
|
||||
void phase2::parse(int type, std::string &&s) {
|
||||
if (_then) _then(type, std::move(s));
|
||||
}
|
||||
|
||||
void phase2::parse(std::string &&line) {
|
||||
|
||||
//fprintf(stderr, "-> %s\n", line.c_str());
|
||||
|
||||
// still needed?
|
||||
if (line.empty()) { finish(); return; }
|
||||
|
||||
|
||||
int cs;
|
||||
const unsigned char *p = (const unsigned char *)line.data();
|
||||
const unsigned char *pe = p + line.size();
|
||||
@ -264,81 +278,19 @@ void phase2::process(const std::string &line) {
|
||||
%% write exec;
|
||||
|
||||
flush();
|
||||
// 2 NLs to make the stack reduce. harmless if in a multi-line constuct.
|
||||
parse(NL, "");
|
||||
parse(NL, "");
|
||||
|
||||
exec();
|
||||
if (_then) {
|
||||
_then(NL, "");
|
||||
_then(NL, "");
|
||||
}
|
||||
}
|
||||
|
||||
void phase2::finish() {
|
||||
parse(0, "");
|
||||
exec();
|
||||
}
|
||||
|
||||
void phase2::parse(int token, std::string &&s) {
|
||||
if (parser) parser->parse(token, std::move(s));
|
||||
}
|
||||
|
||||
void phase2::exec() {
|
||||
|
||||
if (pipe_to && parser) {
|
||||
command_ptr_vector tmp;
|
||||
for (auto &p : parser->command_queue) {
|
||||
if (p) tmp.emplace_back(std::move(p));
|
||||
}
|
||||
parser->command_queue.clear();
|
||||
|
||||
if (!tmp.empty()) pipe_to(std::move(tmp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
phase2::phase2() {
|
||||
parser = phase2_parser::make();
|
||||
//parser->trace(stdout, " ] ");
|
||||
|
||||
}
|
||||
|
||||
void phase2::abort() {
|
||||
parser = nullptr;
|
||||
parser = phase2_parser::make();
|
||||
}
|
||||
|
||||
#pragma mark - phase2_parser
|
||||
|
||||
void phase2_parser::parse_accept() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase2_parser::parse_failure() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase2_parser::syntax_error(int yymajor, std::string &yyminor) {
|
||||
/*
|
||||
switch (yymajor) {
|
||||
case END:
|
||||
fprintf(stderr, "### MPW Shell - Extra END command.\n");
|
||||
break;
|
||||
|
||||
case RPAREN:
|
||||
fprintf(stderr, "### MPW Shell - Extra ) command.\n");
|
||||
break;
|
||||
|
||||
case ELSE:
|
||||
case ELSE_IF:
|
||||
fprintf(stderr, "### MPW Shell - ELSE must be within IF ... END.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "### Parse error near %s\n", yyminor.c_str());
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF");
|
||||
error = true;
|
||||
void phase2::reset() {
|
||||
|
||||
type = 0;
|
||||
pcount = 0;
|
||||
scratch.clear();
|
||||
}
|
||||
|
||||
|
1862
phase3.cpp
Normal file
1862
phase3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
20
phase3.h
Normal file
20
phase3.h
Normal file
@ -0,0 +1,20 @@
|
||||
#define PIPE_PIPE 1
|
||||
#define AMP_AMP 2
|
||||
#define PIPE 3
|
||||
#define SEMI 4
|
||||
#define NL 5
|
||||
#define COMMAND 6
|
||||
#define EVALUATE 7
|
||||
#define BREAK 8
|
||||
#define CONTINUE 9
|
||||
#define EXIT 10
|
||||
#define ERROR 11
|
||||
#define LPAREN 12
|
||||
#define RPAREN 13
|
||||
#define BEGIN 14
|
||||
#define END 15
|
||||
#define LOOP 16
|
||||
#define FOR 17
|
||||
#define IF 18
|
||||
#define ELSE_IF 19
|
||||
#define ELSE 20
|
@ -6,19 +6,19 @@
|
||||
|
||||
%include {
|
||||
|
||||
#include "phase2.h"
|
||||
#include "phase3_parser.h"
|
||||
#include "command.h"
|
||||
#define LEMON_SUPER phase2_parser
|
||||
#include "phase2-parser.h"
|
||||
#define LEMON_SUPER phase3
|
||||
}
|
||||
|
||||
|
||||
%code {
|
||||
|
||||
std::unique_ptr<phase2_parser> phase2_parser::make() {
|
||||
std::unique_ptr<phase3> phase3::make() {
|
||||
return std::make_unique<yypParser>();
|
||||
}
|
||||
|
||||
bool phase2_parser::continuation() const {
|
||||
bool phase3::continuation() const {
|
||||
yypParser *self = (yypParser *)this;
|
||||
|
||||
for (const auto &e : *self) {
|
||||
@ -36,6 +36,43 @@ bool phase2_parser::continuation() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void phase3::parse_accept() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase3::parse_failure() {
|
||||
error = false;
|
||||
}
|
||||
|
||||
void phase3::syntax_error(int yymajor, std::string &yyminor) {
|
||||
/*
|
||||
switch (yymajor) {
|
||||
case END:
|
||||
fprintf(stderr, "### MPW Shell - Extra END command.\n");
|
||||
break;
|
||||
|
||||
case RPAREN:
|
||||
fprintf(stderr, "### MPW Shell - Extra ) command.\n");
|
||||
break;
|
||||
|
||||
case ELSE:
|
||||
case ELSE_IF:
|
||||
fprintf(stderr, "### MPW Shell - ELSE must be within IF ... END.\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "### Parse error near %s\n", yyminor.c_str());
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF");
|
||||
error = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
%left PIPE_PIPE AMP_AMP.
|
||||
@ -102,6 +139,7 @@ term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move
|
||||
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
||||
term(RV) ::= BREAK(C). { RV = std::make_unique<break_command>(std::move(C)); }
|
||||
term(RV) ::= CONTINUE(C). { RV = std::make_unique<continue_command>(std::move(C)); }
|
||||
term(RV) ::= EXIT(C). { RV = std::make_unique<exit_command>(std::move(C)); }
|
||||
term(C) ::= if_command(C).
|
||||
term(C) ::= begin_command(C).
|
||||
term(C) ::= paren_command(C).
|
822
phase3.out
Normal file
822
phase3.out
Normal file
@ -0,0 +1,822 @@
|
||||
State 0:
|
||||
start ::= * command_list
|
||||
(21) command_list ::= *
|
||||
command_list ::= * command_list sep
|
||||
command_list ::= * command_list command sep
|
||||
|
||||
start accept
|
||||
command_list shift 5
|
||||
{default} reduce 21 command_list ::=
|
||||
|
||||
State 1:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= IF sep compound_list * END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
if_command ::= IF sep compound_list * else_command END
|
||||
else_command ::= * ELSE_IF|ELSE sep compound_list
|
||||
else_command ::= * else_command ELSE_IF|ELSE sep compound_list
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
END shift-reduce 16 if_command ::= IF sep compound_list END
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
ELSE_IF shift 16
|
||||
ELSE shift 16
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 13
|
||||
term shift 13 /* because term==command */
|
||||
if_command shift 13 /* because if_command==term */
|
||||
begin_command shift 13 /* because begin_command==term */
|
||||
paren_command shift 13 /* because paren_command==term */
|
||||
loop_command shift 13 /* because loop_command==term */
|
||||
for_command shift 13 /* because for_command==term */
|
||||
else_command shift 31
|
||||
|
||||
State 2:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
for_command ::= FOR sep compound_list * END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
END shift-reduce 15 for_command ::= FOR sep compound_list END
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 13
|
||||
term shift 13 /* because term==command */
|
||||
if_command shift 13 /* because if_command==term */
|
||||
begin_command shift 13 /* because begin_command==term */
|
||||
paren_command shift 13 /* because paren_command==term */
|
||||
loop_command shift 13 /* because loop_command==term */
|
||||
for_command shift 13 /* because for_command==term */
|
||||
|
||||
State 3:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
loop_command ::= LOOP sep compound_list * END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
END shift-reduce 14 loop_command ::= LOOP sep compound_list END
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 13
|
||||
term shift 13 /* because term==command */
|
||||
if_command shift 13 /* because if_command==term */
|
||||
begin_command shift 13 /* because begin_command==term */
|
||||
paren_command shift 13 /* because paren_command==term */
|
||||
loop_command shift 13 /* because loop_command==term */
|
||||
for_command shift 13 /* because for_command==term */
|
||||
|
||||
State 4:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
begin_command ::= BEGIN sep compound_list * END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
END shift-reduce 13 begin_command ::= BEGIN sep compound_list END
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 13
|
||||
term shift 13 /* because term==command */
|
||||
if_command shift 13 /* because if_command==term */
|
||||
begin_command shift 13 /* because begin_command==term */
|
||||
paren_command shift 13 /* because paren_command==term */
|
||||
loop_command shift 13 /* because loop_command==term */
|
||||
for_command shift 13 /* because for_command==term */
|
||||
|
||||
State 5:
|
||||
(20) start ::= command_list *
|
||||
command_list ::= command_list * sep
|
||||
command_list ::= command_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
|
||||
$ reduce 20 start ::= command_list
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 22 command_list ::= command_list sep
|
||||
command shift 15
|
||||
term shift 15 /* because term==command */
|
||||
if_command shift 15 /* because if_command==term */
|
||||
begin_command shift 15 /* because begin_command==term */
|
||||
paren_command shift 15 /* because paren_command==term */
|
||||
loop_command shift 15 /* because loop_command==term */
|
||||
for_command shift 15 /* because for_command==term */
|
||||
|
||||
State 6:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
(18) else_command ::= ELSE_IF|ELSE sep compound_list *
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 13
|
||||
term shift 13 /* because term==command */
|
||||
if_command shift 13 /* because if_command==term */
|
||||
begin_command shift 13 /* because begin_command==term */
|
||||
paren_command shift 13 /* because paren_command==term */
|
||||
loop_command shift 13 /* because loop_command==term */
|
||||
for_command shift 13 /* because for_command==term */
|
||||
{default} reduce 18 else_command ::= ELSE_IF|ELSE sep compound_list
|
||||
|
||||
State 7:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
(19) else_command ::= else_command ELSE_IF|ELSE sep compound_list *
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 13
|
||||
term shift 13 /* because term==command */
|
||||
if_command shift 13 /* because if_command==term */
|
||||
begin_command shift 13 /* because begin_command==term */
|
||||
paren_command shift 13 /* because paren_command==term */
|
||||
loop_command shift 13 /* because loop_command==term */
|
||||
for_command shift 13 /* because for_command==term */
|
||||
{default} reduce 19 else_command ::= else_command ELSE_IF|ELSE sep compound_list
|
||||
|
||||
State 8:
|
||||
compound_list ::= compound_list * sep
|
||||
compound_list ::= compound_list * command sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
(33) paren_list ::= compound_list *
|
||||
paren_list ::= compound_list * command
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
sep shift-reduce 24 compound_list ::= compound_list sep
|
||||
command shift 14
|
||||
term shift 14 /* because term==command */
|
||||
if_command shift 14 /* because if_command==term */
|
||||
begin_command shift 14 /* because begin_command==term */
|
||||
paren_command shift 14 /* because paren_command==term */
|
||||
loop_command shift 14 /* because loop_command==term */
|
||||
for_command shift 14 /* because for_command==term */
|
||||
{default} reduce 33 paren_list ::= compound_list
|
||||
|
||||
State 9:
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= command PIPE opt_nl * command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
opt_nl ::= opt_nl * NL
|
||||
|
||||
NL shift-reduce 35 opt_nl ::= opt_nl NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
command shift-reduce 4 command ::= command PIPE opt_nl command
|
||||
term shift-reduce 4 command ::= command PIPE opt_nl command /* because term==command */
|
||||
if_command shift-reduce 4 command ::= command PIPE opt_nl command /* because if_command==term */
|
||||
begin_command shift-reduce 4 command ::= command PIPE opt_nl command /* because begin_command==term */
|
||||
paren_command shift-reduce 4 command ::= command PIPE opt_nl command /* because paren_command==term */
|
||||
loop_command shift-reduce 4 command ::= command PIPE opt_nl command /* because loop_command==term */
|
||||
for_command shift-reduce 4 command ::= command PIPE opt_nl command /* because for_command==term */
|
||||
|
||||
State 10:
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= command AMP_AMP opt_nl * command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
opt_nl ::= opt_nl * NL
|
||||
|
||||
NL shift-reduce 35 opt_nl ::= opt_nl NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
command shift 33
|
||||
term shift 33 /* because term==command */
|
||||
if_command shift 33 /* because if_command==term */
|
||||
begin_command shift 33 /* because begin_command==term */
|
||||
paren_command shift 33 /* because paren_command==term */
|
||||
loop_command shift 33 /* because loop_command==term */
|
||||
for_command shift 33 /* because for_command==term */
|
||||
|
||||
State 11:
|
||||
command ::= * command PIPE_PIPE opt_nl command
|
||||
command ::= command PIPE_PIPE opt_nl * command
|
||||
command ::= * command AMP_AMP opt_nl command
|
||||
command ::= * command PIPE opt_nl command
|
||||
command ::= * term
|
||||
term ::= * COMMAND
|
||||
term ::= * EVALUATE
|
||||
term ::= * BREAK
|
||||
term ::= * CONTINUE
|
||||
term ::= * EXIT
|
||||
term ::= * if_command
|
||||
term ::= * begin_command
|
||||
term ::= * paren_command
|
||||
term ::= * loop_command
|
||||
term ::= * for_command
|
||||
term ::= * ERROR
|
||||
paren_command ::= * LPAREN paren_list RPAREN
|
||||
begin_command ::= * BEGIN sep compound_list END
|
||||
loop_command ::= * LOOP sep compound_list END
|
||||
for_command ::= * FOR sep compound_list END
|
||||
if_command ::= * IF sep compound_list END
|
||||
if_command ::= * IF sep compound_list else_command END
|
||||
opt_nl ::= opt_nl * NL
|
||||
|
||||
NL shift-reduce 35 opt_nl ::= opt_nl NL
|
||||
COMMAND shift-reduce 5 term ::= COMMAND
|
||||
EVALUATE shift-reduce 6 term ::= EVALUATE
|
||||
BREAK shift-reduce 7 term ::= BREAK
|
||||
CONTINUE shift-reduce 8 term ::= CONTINUE
|
||||
EXIT shift-reduce 9 term ::= EXIT
|
||||
ERROR shift-reduce 10 term ::= ERROR
|
||||
LPAREN shift 12
|
||||
BEGIN shift 21
|
||||
LOOP shift 20
|
||||
FOR shift 19
|
||||
IF shift 18
|
||||
command shift 34
|
||||
term shift 34 /* because term==command */
|
||||
if_command shift 34 /* because if_command==term */
|
||||
begin_command shift 34 /* because begin_command==term */
|
||||
paren_command shift 34 /* because paren_command==term */
|
||||
loop_command shift 34 /* because loop_command==term */
|
||||
for_command shift 34 /* because for_command==term */
|
||||
|
||||
State 12:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
paren_list ::= * compound_list
|
||||
paren_list ::= * compound_list command
|
||||
paren_command ::= LPAREN * paren_list RPAREN
|
||||
|
||||
compound_list shift 8
|
||||
paren_list shift 32
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 13:
|
||||
compound_list ::= compound_list command * sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= command * PIPE_PIPE opt_nl command
|
||||
command ::= command * AMP_AMP opt_nl command
|
||||
command ::= command * PIPE opt_nl command
|
||||
|
||||
PIPE_PIPE shift 30
|
||||
AMP_AMP shift 29
|
||||
PIPE shift 28
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift-reduce 1 compound_list ::= compound_list command sep
|
||||
|
||||
State 14:
|
||||
compound_list ::= compound_list command * sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= command * PIPE_PIPE opt_nl command
|
||||
command ::= command * AMP_AMP opt_nl command
|
||||
command ::= command * PIPE opt_nl command
|
||||
(11) paren_list ::= compound_list command *
|
||||
|
||||
PIPE_PIPE shift 30
|
||||
AMP_AMP shift 29
|
||||
PIPE shift 28
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift-reduce 1 compound_list ::= compound_list command sep
|
||||
{default} reduce 11 paren_list ::= compound_list command
|
||||
|
||||
State 15:
|
||||
command_list ::= command_list command * sep
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
command ::= command * PIPE_PIPE opt_nl command
|
||||
command ::= command * AMP_AMP opt_nl command
|
||||
command ::= command * PIPE opt_nl command
|
||||
|
||||
PIPE_PIPE shift 30
|
||||
AMP_AMP shift 29
|
||||
PIPE shift 28
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift-reduce 0 command_list ::= command_list command sep
|
||||
|
||||
State 16:
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
else_command ::= ELSE_IF|ELSE * sep compound_list
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift 22
|
||||
|
||||
State 17:
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
else_command ::= else_command ELSE_IF|ELSE * sep compound_list
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift 23
|
||||
|
||||
State 18:
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
if_command ::= IF * sep compound_list END
|
||||
if_command ::= IF * sep compound_list else_command END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift 24
|
||||
|
||||
State 19:
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
for_command ::= FOR * sep compound_list END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift 25
|
||||
|
||||
State 20:
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
loop_command ::= LOOP * sep compound_list END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift 26
|
||||
|
||||
State 21:
|
||||
sep ::= * SEMI
|
||||
sep ::= * NL
|
||||
begin_command ::= BEGIN * sep compound_list END
|
||||
|
||||
SEMI shift-reduce 25 sep ::= SEMI
|
||||
NL shift-reduce 26 sep ::= NL
|
||||
sep shift 27
|
||||
|
||||
State 22:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
else_command ::= ELSE_IF|ELSE sep * compound_list
|
||||
|
||||
compound_list shift 6
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 23:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
else_command ::= else_command ELSE_IF|ELSE sep * compound_list
|
||||
|
||||
compound_list shift 7
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 24:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
if_command ::= IF sep * compound_list END
|
||||
if_command ::= IF sep * compound_list else_command END
|
||||
|
||||
compound_list shift 1
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 25:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
for_command ::= FOR sep * compound_list END
|
||||
|
||||
compound_list shift 2
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 26:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
loop_command ::= LOOP sep * compound_list END
|
||||
|
||||
compound_list shift 3
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 27:
|
||||
(23) compound_list ::= *
|
||||
compound_list ::= * compound_list sep
|
||||
compound_list ::= * compound_list command sep
|
||||
begin_command ::= BEGIN sep * compound_list END
|
||||
|
||||
compound_list shift 4
|
||||
{default} reduce 23 compound_list ::=
|
||||
|
||||
State 28:
|
||||
command ::= command PIPE * opt_nl command
|
||||
(34) opt_nl ::= *
|
||||
opt_nl ::= * opt_nl NL
|
||||
|
||||
opt_nl shift 9
|
||||
{default} reduce 34 opt_nl ::=
|
||||
|
||||
State 29:
|
||||
command ::= command AMP_AMP * opt_nl command
|
||||
(34) opt_nl ::= *
|
||||
opt_nl ::= * opt_nl NL
|
||||
|
||||
opt_nl shift 10
|
||||
{default} reduce 34 opt_nl ::=
|
||||
|
||||
State 30:
|
||||
command ::= command PIPE_PIPE * opt_nl command
|
||||
(34) opt_nl ::= *
|
||||
opt_nl ::= * opt_nl NL
|
||||
|
||||
opt_nl shift 11
|
||||
{default} reduce 34 opt_nl ::=
|
||||
|
||||
State 31:
|
||||
if_command ::= IF sep compound_list else_command * END
|
||||
else_command ::= else_command * ELSE_IF|ELSE sep compound_list
|
||||
|
||||
END shift-reduce 17 if_command ::= IF sep compound_list else_command END
|
||||
ELSE_IF shift 17
|
||||
ELSE shift 17
|
||||
|
||||
State 32:
|
||||
paren_command ::= LPAREN paren_list * RPAREN
|
||||
|
||||
RPAREN shift-reduce 12 paren_command ::= LPAREN paren_list RPAREN
|
||||
|
||||
State 33:
|
||||
command ::= command * PIPE_PIPE opt_nl command
|
||||
command ::= command * AMP_AMP opt_nl command
|
||||
(3) command ::= command AMP_AMP opt_nl command *
|
||||
command ::= command * PIPE opt_nl command
|
||||
|
||||
PIPE shift 28
|
||||
{default} reduce 3 command ::= command AMP_AMP opt_nl command
|
||||
|
||||
State 34:
|
||||
command ::= command * PIPE_PIPE opt_nl command
|
||||
(2) command ::= command PIPE_PIPE opt_nl command *
|
||||
command ::= command * AMP_AMP opt_nl command
|
||||
command ::= command * PIPE opt_nl command
|
||||
|
||||
PIPE shift 28
|
||||
{default} reduce 2 command ::= command PIPE_PIPE opt_nl command
|
||||
|
||||
----------------------------------------------------
|
||||
Symbols:
|
||||
0: $:
|
||||
1: PIPE_PIPE
|
||||
2: AMP_AMP
|
||||
3: PIPE
|
||||
4: SEMI
|
||||
5: NL
|
||||
6: COMMAND
|
||||
7: EVALUATE
|
||||
8: BREAK
|
||||
9: CONTINUE
|
||||
10: EXIT
|
||||
11: ERROR
|
||||
12: LPAREN
|
||||
13: RPAREN
|
||||
14: BEGIN
|
||||
15: END
|
||||
16: LOOP
|
||||
17: FOR
|
||||
18: IF
|
||||
19: ELSE_IF
|
||||
20: ELSE
|
||||
21: error:
|
||||
22: start: <lambda> SEMI NL COMMAND EVALUATE BREAK CONTINUE EXIT ERROR LPAREN BEGIN LOOP FOR IF
|
||||
23: command_list: <lambda> SEMI NL COMMAND EVALUATE BREAK CONTINUE EXIT ERROR LPAREN BEGIN LOOP FOR IF
|
||||
24: sep: SEMI NL
|
||||
25: command: COMMAND EVALUATE BREAK CONTINUE EXIT ERROR LPAREN BEGIN LOOP FOR IF
|
||||
26: compound_list: <lambda> SEMI NL COMMAND EVALUATE BREAK CONTINUE EXIT ERROR LPAREN BEGIN LOOP FOR IF
|
||||
27: opt_nl: <lambda> NL
|
||||
28: term: COMMAND EVALUATE BREAK CONTINUE EXIT ERROR LPAREN BEGIN LOOP FOR IF
|
||||
29: if_command: IF
|
||||
30: begin_command: BEGIN
|
||||
31: paren_command: LPAREN
|
||||
32: loop_command: LOOP
|
||||
33: for_command: FOR
|
||||
34: paren_list: <lambda> SEMI NL COMMAND EVALUATE BREAK CONTINUE EXIT ERROR LPAREN BEGIN LOOP FOR IF
|
||||
35: else_command: ELSE_IF ELSE
|
43
phase3_parser.h
Normal file
43
phase3_parser.h
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
#ifndef __phase3_h__
|
||||
#define __phase3_h__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "command.h"
|
||||
#include <lemon_base.h>
|
||||
|
||||
class phase3 : public lemon_base<std::string> {
|
||||
|
||||
public:
|
||||
static std::unique_ptr<phase3> make();
|
||||
|
||||
virtual void syntax_error(int yymajor, std::string &yyminor) override final;
|
||||
virtual void parse_accept() override final;
|
||||
virtual void parse_failure() override final;
|
||||
|
||||
bool continuation() const;
|
||||
|
||||
//void parse(int type, std::string &&s) override final;
|
||||
|
||||
private:
|
||||
|
||||
phase3(const phase3 &) = delete;
|
||||
phase3(phase3 &&) = delete;
|
||||
|
||||
phase3& operator=(const phase3 &) = delete;
|
||||
phase3& operator=(phase3 &&) = delete;
|
||||
|
||||
protected:
|
||||
// these need to be accessible to the lemon-generated parser.
|
||||
phase3() = default;
|
||||
|
||||
command_ptr_vector command_queue;
|
||||
bool error = false;
|
||||
|
||||
friend class mpw_parser;
|
||||
};
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user