mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2024-06-18 14:29:32 +00:00
Compare commits
122 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
a4e724a1a6 | ||
|
fbcbfffcb5 | ||
|
50ac7355bd | ||
|
827f49c48b | ||
|
e2affa1bdd | ||
|
76980a6e06 | ||
|
ba0fe6268f | ||
|
47b734a5fa | ||
|
f9bbf7a2f8 | ||
|
8c8a768530 | ||
|
27c4eadf93 | ||
|
ca54485061 | ||
|
f8c596668f | ||
|
469f0a23c1 | ||
|
9d5d3ca9e8 | ||
|
5b343cc7dd | ||
|
56f945ce29 | ||
|
b9782a0926 | ||
|
ed341db9fa | ||
|
edcb832c13 | ||
|
c2c41f3a52 | ||
|
413b9a805b | ||
|
a2a48fcba7 | ||
|
ad523f258e | ||
|
544f3a994c | ||
|
683b06b3b5 | ||
|
9b16e98133 | ||
|
fe76877693 | ||
|
47af010ba5 | ||
|
1a98acb756 | ||
|
933f23de91 | ||
|
62612a10d3 | ||
|
05f48c6a3c | ||
|
0524d10590 | ||
|
2fdca6ea9d | ||
|
9b577bdbb1 | ||
|
84737e1cf7 | ||
|
f97625eba7 | ||
|
a366bbf2cb | ||
|
76d5c9a474 | ||
|
db8e9af504 | ||
|
fac76f1b54 | ||
|
5c333cbedd | ||
|
34900a00b8 | ||
|
da92eb4e36 | ||
|
c28cfb0710 | ||
|
31f33096cb | ||
|
6d929aa87f | ||
|
80aaaa208d | ||
|
fed90b3753 | ||
|
0ba9574d2d | ||
|
40a92c7976 | ||
|
5f9293a9e2 | ||
|
7984ccca54 | ||
|
42806cdd9b | ||
|
686bee2578 | ||
|
adbf776d31 | ||
|
b4db751cbe | ||
|
84b24e6379 | ||
|
e51e757556 | ||
|
bc2381a360 | ||
|
a44d2d3e4d | ||
|
45eade7af5 | ||
|
6bfad57a35 | ||
|
ca6d8a453e | ||
|
a15c6fbd65 | ||
|
ef99bb40de | ||
|
d56d689f98 | ||
|
8a2b9ec3cd | ||
|
c8f1e370dc | ||
|
92ddf18766 | ||
|
701786277b | ||
|
97bcf8259d | ||
|
322a32af65 | ||
|
edd80fc3c5 | ||
|
7724ca0d7c | ||
|
6ff7b50a7d | ||
|
fafb08b90a | ||
|
56b0c93fd7 |
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@v2
|
||||||
|
|
||||||
|
- 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@v2
|
||||||
|
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@v2
|
||||||
|
|
||||||
|
- name: apt-get
|
||||||
|
run: sudo apt-get 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
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
shell: bash
|
||||||
|
run: cmake --build . --config $BUILD_TYPE
|
|
@ -14,6 +14,5 @@ script: make
|
||||||
before_install:
|
before_install:
|
||||||
- brew update
|
- brew update
|
||||||
- brew install ragel
|
- brew install ragel
|
||||||
- brew install cmake
|
- brew install "https://raw.githubusercontent.com/ksherlock/homebrew-ksherlock/master/lemonxx.rb"
|
||||||
- brew install "https://github.com/ksherlock/lemon--/raw/master/lemon++.rb"
|
|
||||||
|
|
||||||
|
|
133
CMakeLists.txt
133
CMakeLists.txt
|
@ -1,25 +1,50 @@
|
||||||
|
|
||||||
# CMAKE_INSTALL_PREFIX defaults to /usr/local.
|
# CMAKE_INSTALL_PREFIX defaults to /usr/local.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.6)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
project("mpw-shell")
|
project("mpw-shell")
|
||||||
set (PROJECT_TYPE "CXX")
|
set (PROJECT_TYPE "CXX")
|
||||||
set (PROJECT_NAME "MPW Shell")
|
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")
|
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()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
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()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR})
|
||||||
add_definitions(-I ${CMAKE_SOURCE_DIR}/)
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# from https://github.com/gsauthof/cmake-ragel
|
# from https://github.com/gsauthof/cmake-ragel
|
||||||
macro(RAGEL_TARGET Name Input Output)
|
macro(RAGEL_TARGET Name Input Output)
|
||||||
|
@ -54,80 +79,60 @@ macro(RAGEL_TARGET Name Input Output)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
|
||||||
|
#RAGEL_TARGET(phase1 phase1.rl phase1.cpp COMPILE_FLAGS "-p -G2")
|
||||||
|
|
||||||
|
|
||||||
#add_custom_command(
|
|
||||||
# OUTPUT mpw-shell-read.cpp
|
|
||||||
# COMMAND ragel -p -G2 -o mpw-shell-read.cpp "${CMAKE_CURRENT_SOURCE_DIR}/mpw-shell-read.rl"
|
|
||||||
# MAIN_DEPENDENCY mpw-shell-read.rl
|
|
||||||
#)
|
|
||||||
|
|
||||||
RAGEL_TARGET(mpw-shell-read mpw-shell-read.rl mpw-shell-read.cpp COMPILE_FLAGS "-p -G2")
|
|
||||||
RAGEL_TARGET(phase1 phase1.rl phase1.cpp COMPILE_FLAGS "-p -G2")
|
|
||||||
RAGEL_TARGET(phase2 phase2.rl phase2.cpp COMPILE_FLAGS "-p -G2")
|
RAGEL_TARGET(phase2 phase2.rl phase2.cpp COMPILE_FLAGS "-p -G2")
|
||||||
RAGEL_TARGET(pathnames pathnames.rl pathnames.cpp COMPILE_FLAGS "-p -G2")
|
RAGEL_TARGET(pathnames pathnames.rl pathnames.cpp COMPILE_FLAGS "-p -G2")
|
||||||
|
RAGEL_TARGET(mpw-shell-token mpw-shell-token.rl mpw-shell-token.cpp COMPILE_FLAGS "-p -G2")
|
||||||
|
RAGEL_TARGET(mpw-shell-expand mpw-shell-expand.rl mpw-shell-expand.cpp COMPILE_FLAGS "-p -G2")
|
||||||
|
RAGEL_TARGET(mpw-shell-quote mpw-shell-quote.rl mpw-shell-quote.cpp COMPILE_FLAGS "-p -G2")
|
||||||
|
RAGEL_TARGET(value value.rl value.cpp COMPILE_FLAGS "-p -G2")
|
||||||
|
|
||||||
|
|
||||||
# need to copy all OUTPUT file to the build dir
|
# need to copy all OUTPUT file to the build dir
|
||||||
add_custom_command(
|
# add_custom_command(
|
||||||
OUTPUT phase2-parser.cpp phase2-parser.h
|
# OUTPUT phase3.cpp phase3.h phase3.out
|
||||||
COMMAND lemon++ -Tlempar.cxx phase2-parser.lemon
|
# COMMAND lemon++ phase3.lemon
|
||||||
COMMAND cp -p phase2-parser.cpp phase2-parser.h ${CMAKE_CURRENT_BINARY_DIR}/
|
# COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/
|
||||||
MAIN_DEPENDENCY phase2-parser.lemon
|
# MAIN_DEPENDENCY phase3.lemon
|
||||||
DEPENDS lempar.cxx
|
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
# )
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_command(
|
find_program(LEMON_PLUSPLUS NAMES lemon++)
|
||||||
OUTPUT mpw-shell-expand.cpp
|
if (LEMON_PLUSPLUS)
|
||||||
COMMAND ragel -p -G2 -o mpw-shell-expand.cpp "${CMAKE_CURRENT_SOURCE_DIR}/mpw-shell-expand.rl"
|
add_custom_command(
|
||||||
MAIN_DEPENDENCY mpw-shell-expand.rl
|
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
|
||||||
add_custom_command(
|
MAIN_DEPENDENCY phase3.lemon
|
||||||
OUTPUT mpw-shell-token.cpp
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
COMMAND ragel -p -G2 -o mpw-shell-token.cpp "${CMAKE_CURRENT_SOURCE_DIR}/mpw-shell-token.rl"
|
)
|
||||||
MAIN_DEPENDENCY mpw-shell-token.rl
|
endif()
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT mpw-shell-command.cpp
|
|
||||||
COMMAND ragel -p -G2 -o mpw-shell-command.cpp "${CMAKE_CURRENT_SOURCE_DIR}/mpw-shell-command.rl"
|
|
||||||
MAIN_DEPENDENCY mpw-shell-command.rl
|
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT value.cpp
|
|
||||||
COMMAND ragel -p -G2 -o value.cpp "${CMAKE_CURRENT_SOURCE_DIR}/value.rl"
|
|
||||||
MAIN_DEPENDENCY value.rl
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT mpw-shell-quote.cpp
|
|
||||||
COMMAND ragel -p -G2 -o mpw-shell-quote.cpp "${CMAKE_CURRENT_SOURCE_DIR}/mpw-shell-quote.rl"
|
|
||||||
MAIN_DEPENDENCY mpw-shell-quote.rl
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# mpw-shell-execute.cpp mpw-shell-builtins.cpp mpw-shell-read.cpp
|
|
||||||
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
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
|
mpw-shell-parser.cpp mpw_parser.cpp value.cpp mpw-shell-quote.cpp mpw-regex.cpp
|
||||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp
|
phase1.cpp phase2.cpp phase3.cpp command.cpp environment.cpp builtins.cpp
|
||||||
pathnames.cpp
|
pathnames.cpp
|
||||||
|
macroman.cpp
|
||||||
cxx/mapped_file.cpp
|
cxx/mapped_file.cpp
|
||||||
cxx/filesystem.cpp
|
cxx/filesystem.cpp
|
||||||
cxx/path.cpp
|
cxx/path.cpp
|
||||||
cxx/directory_iterator.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
|
# create a symlink for mpw-make
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|
90
README.md
Normal file
90
README.md
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
MPW Shell
|
||||||
|
---------
|
||||||
|
|
||||||
|
MPW Shell is a re-implementation of the Macintosh Programmer's Workshop shell.
|
||||||
|
The primary reason is to support MPW Make (which generated shell script). It
|
||||||
|
may also be useful for other things.
|
||||||
|
|
||||||
|
Supported features
|
||||||
|
------------------
|
||||||
|
* If ... [Else If] ... [Else] ... End
|
||||||
|
* Begin ... End
|
||||||
|
* Loop ... End
|
||||||
|
* For name In [word...] ... End
|
||||||
|
* Break [If], Continue [If], Exit [If]
|
||||||
|
* ( ... )
|
||||||
|
* ||
|
||||||
|
* &&
|
||||||
|
* Redirection
|
||||||
|
* | "pipes" (via a temporary file. Presumably, that's what MPW did as well.)
|
||||||
|
* Subshells (`...`, ``...``)
|
||||||
|
|
||||||
|
|
||||||
|
Not (yet) supported
|
||||||
|
-------------
|
||||||
|
* aliases
|
||||||
|
* regular expressions
|
||||||
|
* text-editing commands (search forward/backward, et cetera)
|
||||||
|
|
||||||
|
Builtin Commands
|
||||||
|
----------------
|
||||||
|
* AboutBox
|
||||||
|
* Alias
|
||||||
|
* Catenate
|
||||||
|
* Directory
|
||||||
|
* Echo
|
||||||
|
* Evaluate
|
||||||
|
* Execute
|
||||||
|
* Exists
|
||||||
|
* Export
|
||||||
|
* Parameters
|
||||||
|
* Quit
|
||||||
|
* Quote
|
||||||
|
* Set
|
||||||
|
* Shift
|
||||||
|
* Unalias
|
||||||
|
* Unexport
|
||||||
|
* Unset
|
||||||
|
* Version
|
||||||
|
* Which
|
||||||
|
|
||||||
|
|
||||||
|
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`. 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`.
|
776
builtins.cpp
776
builtins.cpp
File diff suppressed because it is too large
Load Diff
19
builtins.h
19
builtins.h
|
@ -8,16 +8,29 @@ class Environment;
|
||||||
class fdmask;
|
class fdmask;
|
||||||
class token;
|
class token;
|
||||||
|
|
||||||
|
int builtin_aboutbox(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
int builtin_catenate(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_directory(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_directory(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_echo(Environment &e, const std::vector<std::string> &, const fdmask &);
|
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_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_quote(Environment &e, const std::vector<std::string> &tokens, const fdmask &);
|
||||||
int builtin_set(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_set(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_unset(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_shift(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_export(Environment &e, const std::vector<std::string> &, const fdmask &);
|
|
||||||
int builtin_unexport(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_unexport(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
int builtin_unset(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
int builtin_version(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_which(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_which(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_aboutbox(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 &);
|
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);
|
||||||
|
|
||||||
|
|
791
command.cpp
791
command.cpp
|
@ -1,25 +1,38 @@
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "phase2-parser.h"
|
#include "phase3.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "cxx/filesystem.h"
|
#include "cxx/filesystem.h"
|
||||||
#include "cxx/string_splitter.h"
|
#include "cxx/string_splitter.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <strings.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
extern volatile int control_c;
|
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern std::atomic<int> control_c;
|
||||||
|
|
||||||
namespace fs = filesystem;
|
namespace fs = filesystem;
|
||||||
extern fs::path mpw_path();
|
extern fs::path mpw_path();
|
||||||
|
@ -29,6 +42,106 @@ namespace ToolBox {
|
||||||
std::string UnixToMac(const std::string path);
|
std::string UnixToMac(const std::string path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::vector<token> token_vector;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns:
|
||||||
|
* <0 -> error
|
||||||
|
* 0 -> false
|
||||||
|
* >0 -> true
|
||||||
|
*/
|
||||||
|
|
||||||
|
int bad_if(const char *name) {
|
||||||
|
fprintf(stderr, "### %s - Missing if keyword.\n", name);
|
||||||
|
fprintf(stderr, "# Usage - %s [if expression...]\n", name);
|
||||||
|
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());
|
||||||
|
|
||||||
|
int32_t e;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
default: return 0;
|
||||||
|
|
||||||
|
// exit [number] [if expr] ([number has been removed])
|
||||||
|
case EXIT:
|
||||||
|
case BREAK:
|
||||||
|
case CONTINUE:
|
||||||
|
case ELSE:
|
||||||
|
tokens.pop_back();
|
||||||
|
|
||||||
|
if (tokens.empty()) return 1;
|
||||||
|
|
||||||
|
if (strcasecmp(tokens.back().string.c_str(), "if") != 0) {
|
||||||
|
const char *name = "";
|
||||||
|
switch(type) {
|
||||||
|
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(env, "If", std::move(tokens));
|
||||||
|
}
|
||||||
|
catch (std::exception &ex) {
|
||||||
|
fprintf(stderr, "%s\n", ex.what());
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return !!e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fs::path which(const Environment &env, const std::string &name) {
|
fs::path which(const Environment &env, const std::string &name) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
|
@ -50,14 +163,17 @@ fs::path which(const Environment &env, const std::string &name) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
|
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
|
||||||
|
|
||||||
|
|
||||||
std::vector<char *> cargv;
|
std::vector<char *> cargv;
|
||||||
cargv.reserve(argv.size() + 3);
|
cargv.reserve(argv.size() + 3);
|
||||||
|
|
||||||
|
|
||||||
cargv.push_back((char *)"mpw");
|
cargv.push_back((char *)"mpw");
|
||||||
//cargv.push_back((char *)"--shell");
|
cargv.push_back((char *)"--shell");
|
||||||
|
|
||||||
unsigned offset = cargv.size();
|
unsigned offset = cargv.size();
|
||||||
|
|
||||||
|
@ -68,21 +184,39 @@ void launch_mpw(const Environment &env, const std::vector<std::string> &argv, co
|
||||||
cargv.push_back(nullptr);
|
cargv.push_back(nullptr);
|
||||||
|
|
||||||
|
|
||||||
// export environment...
|
// export environment...
|
||||||
|
|
||||||
for (const auto &kv : env) {
|
for (const auto &kv : env) {
|
||||||
if (kv.second) { // exported
|
if (kv.second) { // exported
|
||||||
std::string name = "mpw$" + kv.first;
|
std::string name = "mpw$" + kv.first;
|
||||||
setenv(name.c_str(), kv.second.c_str(), 1);
|
setenv(name.c_str(), kv.second.c_str(), 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handle any indirection...
|
// handle any indirection...
|
||||||
fds.dup();
|
fds.dup();
|
||||||
|
|
||||||
execv(mpw_path().c_str(), cargv.data());
|
|
||||||
perror("execvp: ");
|
// re-set all signal handlers.
|
||||||
exit(EX_OSERR); // raise a signal?
|
|
||||||
|
/*
|
||||||
|
* execv modifies handlers as such:
|
||||||
|
* blocked -> blocked
|
||||||
|
* ignored -> ignored
|
||||||
|
* caught -> default action
|
||||||
|
*/
|
||||||
|
struct sigaction sig_action;
|
||||||
|
|
||||||
|
sig_action.sa_handler = SIG_DFL;
|
||||||
|
sig_action.sa_flags = 0;
|
||||||
|
sigemptyset(&sig_action.sa_mask);
|
||||||
|
for (int i = 1; i < NSIG; ++i) {
|
||||||
|
sigaction(i, &sig_action, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
execv(mpw_path().c_str(), cargv.data());
|
||||||
|
perror("execvp: ");
|
||||||
|
exit(EX_OSERR); // raise a signal?
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -94,29 +228,46 @@ namespace {
|
||||||
|
|
||||||
std::unordered_map<std::string, int (*)(Environment &, const std::vector<std::string> &, const fdmask &)> builtins = {
|
std::unordered_map<std::string, int (*)(Environment &, const std::vector<std::string> &, const fdmask &)> builtins = {
|
||||||
{"aboutbox", builtin_aboutbox},
|
{"aboutbox", builtin_aboutbox},
|
||||||
|
{"alias", builtin_alias},
|
||||||
|
{"catenate", builtin_catenate},
|
||||||
{"directory", builtin_directory},
|
{"directory", builtin_directory},
|
||||||
{"echo", builtin_echo},
|
{"echo", builtin_echo},
|
||||||
|
{"execute", builtin_execute},
|
||||||
|
{"exists", builtin_exists},
|
||||||
{"export", builtin_export},
|
{"export", builtin_export},
|
||||||
|
{"help", builtin_help},
|
||||||
{"parameters", builtin_parameters},
|
{"parameters", builtin_parameters},
|
||||||
|
{"quit", builtin_quit},
|
||||||
{"quote", builtin_quote},
|
{"quote", builtin_quote},
|
||||||
{"set", builtin_set},
|
{"set", builtin_set},
|
||||||
|
{"shift", builtin_shift},
|
||||||
|
{"unalias", builtin_unalias},
|
||||||
{"unexport", builtin_unexport},
|
{"unexport", builtin_unexport},
|
||||||
{"unset", builtin_unset},
|
{"unset", builtin_unset},
|
||||||
|
{"version", builtin_version},
|
||||||
{"which", builtin_which},
|
{"which", builtin_which},
|
||||||
|
|
||||||
|
// not in MPW.
|
||||||
|
{"true", builtin_true},
|
||||||
|
{"false", builtin_false},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int execute_external(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
|
int execute_external(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
int pid;
|
int pid;
|
||||||
|
|
||||||
|
struct sigaction ign, intact, quitact;
|
||||||
|
sigset_t newsigblock, oldsigblock;
|
||||||
|
|
||||||
|
sigemptyset(&newsigblock);
|
||||||
|
sigaddset(&newsigblock, SIGCHLD);
|
||||||
|
sigaddset(&newsigblock, SIGINT);
|
||||||
|
sigaddset(&newsigblock, SIGQUIT);
|
||||||
|
sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
|
||||||
|
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
|
@ -124,13 +275,20 @@ namespace {
|
||||||
exit(EX_OSERR);
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
|
//sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
|
||||||
launch_mpw(env, argv, fds);
|
launch_mpw(env, argv, fds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ignore int/quit while waiting on child */
|
||||||
|
|
||||||
|
memset(&ign, 0, sizeof(ign));
|
||||||
|
ign.sa_handler = SIG_IGN;
|
||||||
|
sigemptyset(&ign.sa_mask);
|
||||||
|
sigaction(SIGINT, &ign, &intact);
|
||||||
|
sigaction(SIGQUIT, &ign, &quitact);
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
int status;
|
|
||||||
pid_t ok;
|
pid_t ok;
|
||||||
ok = waitpid(pid, &status, 0);
|
ok = waitpid(pid, &status, 0);
|
||||||
if (ok < 0) {
|
if (ok < 0) {
|
||||||
|
@ -138,23 +296,29 @@ namespace {
|
||||||
perror("waitpid:");
|
perror("waitpid:");
|
||||||
exit(EX_OSERR);
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
if (WIFEXITED(status)) return WEXITSTATUS(status);
|
|
||||||
if (WIFSIGNALED(status)) return -9; // command-. user abort exits via -9.
|
|
||||||
|
|
||||||
fprintf(stderr, "waitpid - unexpected result\n");
|
|
||||||
exit(EX_OSERR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sigaction(SIGINT, &intact, NULL);
|
||||||
|
sigaction(SIGQUIT, &quitact, NULL);
|
||||||
|
sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
|
||||||
|
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WIFSIGNALED(status)) {
|
||||||
|
return -9; // command-. user abort exits via -9.
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "waitpid - unexpected result\n");
|
||||||
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//std::string expand_vars(const std::string &s, const class Environment &);
|
|
||||||
|
|
||||||
command::~command()
|
command::~command()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -163,83 +327,209 @@ command::~command()
|
||||||
* echo and error should respect the active fdmask.
|
* echo and error should respect the active fdmask.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
int exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||||
|
|
||||||
|
bool echo = true;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
if (control_c) throw execution_of_input_terminated();
|
||||||
|
|
||||||
std::string s = expand_vars(text, env);
|
|
||||||
|
|
||||||
|
|
||||||
process p;
|
|
||||||
try {
|
try {
|
||||||
auto tokens = tokenize(s, false);
|
process p;
|
||||||
|
command = expand_vars(command, env, fds);
|
||||||
|
auto tokens = tokenize(command, false);
|
||||||
if (tokens.empty()) return 0;
|
if (tokens.empty()) return 0;
|
||||||
parse_tokens(std::move(tokens), p);
|
parse_tokens(std::move(tokens), p);
|
||||||
} catch(std::exception &e) {
|
env.echo("%s", command.c_str());
|
||||||
fprintf(stderr, "%s\n", e.what());
|
echo = false;
|
||||||
|
|
||||||
|
if (p.arguments.empty()) return env.status(0);
|
||||||
|
|
||||||
|
rv = fx(p);
|
||||||
|
}
|
||||||
|
catch (mpw_error &e) {
|
||||||
|
if (echo) env.echo("%s", command.c_str());
|
||||||
|
fprintf(stderr, "### %s\n", e.what());
|
||||||
|
return env.status(e.status(), throwup);
|
||||||
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
if (echo) env.echo("%s", command.c_str());
|
||||||
|
fprintf(stderr, "### %s\n", e.what());
|
||||||
return env.status(-4, throwup);
|
return env.status(-4, throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.arguments.empty()) return 0;
|
return env.status(rv, throwup);
|
||||||
|
}
|
||||||
env.echo("%s", s.c_str());
|
|
||||||
|
|
||||||
fdmask newfds = p.fds | fds;
|
int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
std::string name = p.arguments.front();
|
|
||||||
lowercase(name);
|
|
||||||
|
|
||||||
auto iter = builtins.find(name);
|
return exec(text, env, fds, throwup, [&](process &p){
|
||||||
if (iter != builtins.end()) {
|
|
||||||
env.set("command", name);
|
fdmask newfds = p.fds | fds;
|
||||||
int status = iter->second(env, p.arguments, newfds);
|
|
||||||
return env.status(status, throwup);
|
std::string name = p.arguments.front();
|
||||||
|
lowercase(name);
|
||||||
|
|
||||||
|
auto iter = builtins.find(name);
|
||||||
|
if (iter != builtins.end()) {
|
||||||
|
env.set("command", name);
|
||||||
|
int status = iter->second(env, p.arguments, newfds);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.startup()) {
|
||||||
|
fprintf(stderr, "### MPW Shell - startup file may not contain external commands.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = p.arguments.front();
|
||||||
|
fs::path path = which(env, name);
|
||||||
|
if (path.empty()) {
|
||||||
|
std::string error = "MPW Shell - Command \"" + name + "\" was not found.";
|
||||||
|
throw mpw_error(-1, error);
|
||||||
|
//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;
|
||||||
|
|
||||||
|
return execute_external(env, p.arguments, newfds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
int eval_exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx){
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool echo = true;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
if (control_c) throw execution_of_input_terminated();
|
||||||
|
|
||||||
|
try {
|
||||||
|
command = expand_vars(command, env, fds);
|
||||||
|
auto tokens = tokenize(command, true);
|
||||||
|
|
||||||
|
if (tokens.empty()) return 0;
|
||||||
|
env.echo("%s", command.c_str());
|
||||||
|
echo = false;
|
||||||
|
name = tokens[0].string;
|
||||||
|
|
||||||
|
rv = fx(tokens);
|
||||||
|
}
|
||||||
|
catch (const exit_command_t &ex) {
|
||||||
|
// convert to execution of input terminated.
|
||||||
|
throw execution_of_input_terminated(ex.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.test()) return env.status(0);
|
catch (mpw_error &e) {
|
||||||
if (env.startup()) {
|
if (echo) env.echo("%s", command.c_str());
|
||||||
fprintf(stderr, "### MPW Shell - startup file may not contain external commands.\n");
|
fprintf(stderr, "### %s\n", e.what());
|
||||||
return env.status(0);
|
return env.status(e.status(), throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
name = p.arguments.front();
|
catch(std::exception &e) {
|
||||||
fs::path path = which(env, name);
|
// these should include the argv0 name.
|
||||||
if (path.empty()) {
|
if (echo) env.echo("%s", command.c_str());
|
||||||
fprintf(stderr, "### MPW Shell - Command \"%s\" was not found.\n", name.c_str());
|
fprintf(stderr, "### %s - %s\n", name.c_str(), e.what());
|
||||||
return env.status(-1, throwup);
|
return env.status(-4, throwup);
|
||||||
}
|
}
|
||||||
env.set("command", path);
|
|
||||||
p.arguments[0] = path;
|
|
||||||
|
|
||||||
int status = execute_external(env, p.arguments, newfds);
|
return env.status(rv, throwup);
|
||||||
|
|
||||||
return env.status(status, throwup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
|
||||||
|
|
||||||
std::string s = expand_vars(text, env);
|
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||||
|
env.set("command", "evaluate");
|
||||||
|
return builtin_evaluate(env, std::move(tokens), fds);
|
||||||
|
});
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (status > 0) throw break_command_t();
|
||||||
|
return status;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (status > 0) throw continue_command_t();
|
||||||
|
return status;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
@ -250,11 +540,13 @@ int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
if (rv == 0) return rv;
|
if (rv == 0) return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.status(rv);
|
return e.status(rv, throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
int and_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
int and_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
// mpw - false && true -> no exit
|
||||||
|
// mpw - true && false -> exit
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
for (auto &c : children) {
|
for (auto &c : children) {
|
||||||
if (!c) continue;
|
if (!c) continue;
|
||||||
|
@ -262,10 +554,40 @@ int and_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
if (rv != 0) return rv;
|
if (rv != 0) return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return e.status(rv, throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int pipe_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
|
// not yet supported!
|
||||||
|
//fputs( "### MPW Shell - Pipes are not yet supported.\n", stderr);
|
||||||
|
//return e.status(1, throwup);
|
||||||
|
|
||||||
|
if (children[0] && children[1]) {
|
||||||
|
int fd;
|
||||||
|
int rv = 0;
|
||||||
|
char temp[32] = "/tmp/mpw-shell-XXXXXXXX";
|
||||||
|
fdset pipe_fd;
|
||||||
|
|
||||||
|
fd = mkstemp(temp);
|
||||||
|
unlink(temp);
|
||||||
|
pipe_fd.set(1, fd);
|
||||||
|
|
||||||
|
rv = children[0]->execute(e, pipe_fd | fds, throwup);
|
||||||
|
|
||||||
|
// fdset will close the fd ...
|
||||||
|
pipe_fd.swap_in_out();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
int vector_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
int vector_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
@ -280,7 +602,12 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
if (control_c) throw execution_of_input_terminated();
|
||||||
|
|
||||||
std::string s = expand_vars(text, e);
|
if (type == ERROR) {
|
||||||
|
fprintf(stderr, "%s\n", text.c_str());
|
||||||
|
return e.status(-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string s = expand_vars(text, e, fds);
|
||||||
|
|
||||||
e.echo("%s", s.c_str());
|
e.echo("%s", s.c_str());
|
||||||
|
|
||||||
|
@ -303,154 +630,212 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
return e.status(-3);
|
return e.status(-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
template<class F>
|
||||||
|
int begin_end_exec(std::string begin, std::string end, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||||
|
|
||||||
int check_ends(const std::string &s, fdmask &fds) {
|
|
||||||
|
|
||||||
// MPW ignores any END tokens other than redirection.
|
bool echo = true;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
if (control_c) throw execution_of_input_terminated();
|
||||||
|
|
||||||
|
try {
|
||||||
process p;
|
process p;
|
||||||
try {
|
begin = expand_vars(begin, env, fds);
|
||||||
auto tokens = tokenize(s, false);
|
end = expand_vars(end, env, fds);
|
||||||
parse_tokens(std::move(tokens), p);
|
|
||||||
}
|
auto b = tokenize(begin, true);
|
||||||
catch (std::exception &e) {
|
auto e = tokenize(end, false);
|
||||||
fprintf(stderr, "%s\n", e.what());
|
|
||||||
return -4;
|
parse_tokens(std::move(e), p);
|
||||||
}
|
|
||||||
fds = p.fds.to_mask();
|
if (echo) env.echo("%s ... %s", begin.c_str(), end.c_str() );
|
||||||
return 0;
|
|
||||||
|
rv = fx(b, p);
|
||||||
}
|
}
|
||||||
|
catch (execution_of_input_terminated &e) {
|
||||||
|
// pass through.
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (mpw_error &e) {
|
||||||
|
if (echo) env.echo("%s ... %s", begin.c_str(), end.c_str() );
|
||||||
|
fprintf(stderr, "### %s\n", e.what());
|
||||||
|
return env.status(e.status(), throwup);
|
||||||
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
if (echo) env.echo("%s ... %s", begin.c_str(), end.c_str() );
|
||||||
|
fprintf(stderr, "### %s\n", e.what());
|
||||||
|
return env.status(-4, throwup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.status(rv, throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
// todo -- parse end for redirection.
|
|
||||||
|
|
||||||
std::string b = expand_vars(begin, env);
|
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||||
std::string e = expand_vars(end, env);
|
|
||||||
|
|
||||||
|
env.set("command", type == BEGIN ? "end" : ")");
|
||||||
env.set("command", type == BEGIN ? "end" : ")");
|
if (b.size() != 1) {
|
||||||
|
fprintf(stderr, "### Begin - Too many parameters were specified.\n");
|
||||||
// echo!
|
fprintf(stderr, "Usage - Begin\n");
|
||||||
env.echo("%s ... %s",
|
return -3;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* returns:
|
|
||||||
* <0 -> error
|
|
||||||
* 0 -> false
|
|
||||||
* >0 -> true
|
|
||||||
*/
|
|
||||||
|
|
||||||
int evaluate(int type, const std::string &s, Environment &env) {
|
|
||||||
|
|
||||||
auto tokens = tokenize(s, true);
|
|
||||||
std::reverse(tokens.begin(), tokens.end());
|
|
||||||
|
|
||||||
int32_t e;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
default: return 0;
|
|
||||||
|
|
||||||
case ELSE_IF:
|
|
||||||
tokens.pop_back();
|
|
||||||
case IF:
|
|
||||||
tokens.pop_back();
|
|
||||||
try {
|
|
||||||
e = evaluate_expression("If", std::move(tokens));
|
|
||||||
}
|
|
||||||
catch (std::exception &ex) {
|
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ELSE:
|
|
||||||
e = 1;
|
|
||||||
if (tokens.size() > 1) {
|
|
||||||
fprintf(stderr, "### Else - Missing if keyword.\n");
|
|
||||||
fprintf(stderr, "# Usage - Else [if expression...]\n");
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return !!e;
|
|
||||||
}
|
fdmask newfds = p.fds | fds;
|
||||||
|
|
||||||
|
int rv;
|
||||||
|
env.indent_and([&]{
|
||||||
|
rv = vector_command::execute(env, newfds);
|
||||||
|
});
|
||||||
|
|
||||||
|
env.echo("%s", type == BEGIN ? "end" : ")");
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
|
env.set("command", "end");
|
||||||
|
if (b.size() != 1) {
|
||||||
|
fprintf(stderr, "### Loop - Too many parameters were specified.\n");
|
||||||
|
fprintf(stderr, "Usage - Loop\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdmask newfds = p.fds | fds;
|
||||||
|
|
||||||
|
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 rv;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
|
env.set("command", "end");
|
||||||
|
|
||||||
|
if (b.size() < 3 || strcasecmp(b[2].string.c_str(), "in")) {
|
||||||
|
fprintf(stderr, "### For - Missing in keyword.\n");
|
||||||
|
fprintf(stderr, "Usage - For name in [word...]\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdmask newfds = p.fds | fds;
|
||||||
|
|
||||||
|
int rv = 0;
|
||||||
|
for (int i = 3; i < b.size(); ++i ) {
|
||||||
|
|
||||||
|
if (control_c) throw execution_of_input_terminated();
|
||||||
|
|
||||||
|
env.set(b[1].string, b[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 rv;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the entire command prints even if there is an error with the expression.
|
* the entire command prints even if there is an error with the expression.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
bool ok = false;
|
int error = 0;
|
||||||
|
bool skip = false;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
std::string e = expand_vars(end, env);
|
|
||||||
|
|
||||||
env.set("command", "end");
|
|
||||||
|
|
||||||
// parse end for indirection.
|
|
||||||
fdmask newfds;
|
fdmask newfds;
|
||||||
int status = check_ends(e, newfds);
|
|
||||||
newfds |= fds;
|
|
||||||
if (status) {
|
|
||||||
rv = status;
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &c : clauses) {
|
for (auto &c : clauses) {
|
||||||
std::string s = expand_vars(c->clause, env);
|
|
||||||
|
|
||||||
if (c->type == IF)
|
int tmp;
|
||||||
env.echo("%s ... %s", s.c_str(), e.c_str());
|
if (first) {
|
||||||
else
|
first = false;
|
||||||
env.echo("%s", s.c_str());
|
tmp = begin_end_exec(c->clause, end, env, fds, false, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
if (ok) continue;
|
newfds = p.fds | fds;
|
||||||
|
|
||||||
int tmp = evaluate(c->type, s, env);
|
int status = evaluate(c->type, std::move(b), env);
|
||||||
if (tmp < 0) { ok = true; rv = tmp; continue; }
|
if (status < 0) {
|
||||||
if (tmp == 0) continue;
|
error = status;
|
||||||
|
}
|
||||||
|
if (status > 0) {
|
||||||
|
skip = true;
|
||||||
|
|
||||||
env.indent_and([&](){
|
env.indent_and([&](){
|
||||||
rv = c->execute(env, newfds);
|
rv = c->execute(env, newfds);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
if (tmp != 0) error = tmp;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// second...
|
||||||
|
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);
|
||||||
|
if (status < 0) {
|
||||||
|
error = status;
|
||||||
|
}
|
||||||
|
if (status > 0) {
|
||||||
|
skip = true;
|
||||||
|
|
||||||
|
env.indent_and([&](){
|
||||||
|
rv = c->execute(env, newfds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
});
|
||||||
|
if (tmp != 0 && !skip) error = tmp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env.echo("end");
|
env.echo("end");
|
||||||
return env.status(rv);
|
if (error) return env.status(error, throwup);
|
||||||
|
return env.status(rv, throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
73
command.h
73
command.h
|
@ -6,7 +6,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "phase2-parser.h"
|
#include "phase3.h"
|
||||||
|
|
||||||
typedef std::unique_ptr<struct command> command_ptr;
|
typedef std::unique_ptr<struct command> command_ptr;
|
||||||
typedef std::vector<command_ptr> command_ptr_vector;
|
typedef std::vector<command_ptr> command_ptr_vector;
|
||||||
|
@ -22,7 +22,7 @@ struct command {
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual bool terminal() const noexcept {
|
virtual bool terminal() const noexcept {
|
||||||
return type == EVALUATE || type == COMMAND;
|
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR || type == EXIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int type = 0;
|
int type = 0;
|
||||||
|
@ -60,6 +60,38 @@ struct evaluate_command : public command {
|
||||||
virtual int execute(Environment &e, const fdmask &fds, bool throwup = true) final override;
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup = true) final override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct break_command : public command {
|
||||||
|
|
||||||
|
template<class S>
|
||||||
|
break_command(S &&s) : command(BREAK), text(std::forward<S>(s))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct continue_command : public command {
|
||||||
|
|
||||||
|
template<class S>
|
||||||
|
continue_command(S &&s) : command(CONTINUE), text(std::forward<S>(s))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
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 {
|
struct binary_command : public command {
|
||||||
|
|
||||||
binary_command(int t, command_ptr &&a, command_ptr &&b) :
|
binary_command(int t, command_ptr &&a, command_ptr &&b) :
|
||||||
|
@ -91,15 +123,13 @@ struct and_command : public binary_command {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
struct pipe_command : public binary_command {
|
struct pipe_command : public binary_command {
|
||||||
and_command(command_ptr &&a, command_ptr &&b) :
|
pipe_command(command_ptr &&a, command_ptr &&b) :
|
||||||
binary_command(PIPE, std::move(a), std::move(b))
|
binary_command(PIPE, std::move(a), std::move(b))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual int execute(Environment &e) final override;
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
struct vector_command : public command {
|
struct vector_command : public command {
|
||||||
|
@ -122,6 +152,33 @@ struct begin_command : public vector_command {
|
||||||
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct loop_command : public vector_command {
|
||||||
|
|
||||||
|
template<class S1, class S2>
|
||||||
|
loop_command(int t, command_ptr_vector &&v, S1 &&b, S2 &&e) :
|
||||||
|
vector_command(t, std::move(v)), begin(std::forward<S1>(b)), end(std::forward<S2>(e))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string begin;
|
||||||
|
std::string end;
|
||||||
|
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct for_command : public vector_command {
|
||||||
|
|
||||||
|
template<class S1, class S2>
|
||||||
|
for_command(int t, command_ptr_vector &&v, S1 &&b, S2 &&e) :
|
||||||
|
vector_command(t, std::move(v)), begin(std::forward<S1>(b)), end(std::forward<S2>(e))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string begin;
|
||||||
|
std::string end;
|
||||||
|
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef std::unique_ptr<struct if_else_clause> if_else_clause_ptr;
|
typedef std::unique_ptr<struct if_else_clause> if_else_clause_ptr;
|
||||||
struct if_command : public command {
|
struct if_command : public command {
|
||||||
|
|
||||||
|
@ -151,4 +208,6 @@ struct if_else_clause : public vector_command {
|
||||||
//bool evaluate(const Environment &e);
|
//bool evaluate(const Environment &e);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
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
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace filesystem {
|
namespace filesystem {
|
||||||
|
|
||||||
|
@ -264,10 +269,10 @@ namespace filesystem {
|
||||||
path current_path(error_code& ec) {
|
path current_path(error_code& ec) {
|
||||||
|
|
||||||
char *cp;
|
char *cp;
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX+1];
|
||||||
|
|
||||||
ec.clear();
|
ec.clear();
|
||||||
cp = ::getcwd(buffer, sizeof(buffer));
|
cp = ::getcwd(buffer, PATH_MAX);
|
||||||
if (cp) return path(cp);
|
if (cp) return path(cp);
|
||||||
|
|
||||||
ec = error_code(errno, std::system_category());
|
ec = error_code(errno, std::system_category());
|
||||||
|
@ -299,7 +304,7 @@ namespace filesystem {
|
||||||
|
|
||||||
path canonical(const path& p, error_code& ec) {
|
path canonical(const path& p, error_code& ec) {
|
||||||
char *cp;
|
char *cp;
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX+1];
|
||||||
|
|
||||||
ec.clear();
|
ec.clear();
|
||||||
cp = realpath(p.c_str(), buffer);
|
cp = realpath(p.c_str(), buffer);
|
||||||
|
@ -311,7 +316,7 @@ namespace filesystem {
|
||||||
path canonical(const path& p, const path& base, error_code& ec) {
|
path canonical(const path& p, const path& base, error_code& ec) {
|
||||||
|
|
||||||
char *cp;
|
char *cp;
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX+1];
|
||||||
|
|
||||||
ec.clear();
|
ec.clear();
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,8 @@ namespace filesystem {
|
||||||
}
|
}
|
||||||
path& remove_filename();
|
path& remove_filename();
|
||||||
path& replace_filename(const path& replacement);
|
path& replace_filename(const path& replacement);
|
||||||
path& replace_extension(const path& replacement = path());
|
path& replace_extension(/* const path& replacement = path() */);
|
||||||
|
path& replace_extension(const path& replacement);
|
||||||
|
|
||||||
void swap(path& rhs) noexcept {
|
void swap(path& rhs) noexcept {
|
||||||
std::swap(_path, rhs._path);
|
std::swap(_path, rhs._path);
|
||||||
|
|
|
@ -23,17 +23,12 @@ namespace {
|
||||||
FX _fx;
|
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
|
#ifdef _WIN32
|
||||||
|
@ -41,12 +36,8 @@ namespace {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void throw_error() {
|
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||||
throw_error(GetLastError());
|
set_or_throw_error(ec, GetLastError(), what);
|
||||||
}
|
|
||||||
|
|
||||||
void throw_error(const std::string &what) {
|
|
||||||
throw_error(GetLastError(), what);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,19 +52,16 @@ 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;
|
HANDLE fh;
|
||||||
HANDLE mh;
|
HANDLE mh;
|
||||||
LARGE_INTEGER file_size;
|
|
||||||
|
|
||||||
// length of 0 in CreateFileMapping / MapViewOfFile
|
// length of 0 in CreateFileMapping / MapViewOfFile
|
||||||
// means map the entire file.
|
// means map the entire file.
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
fh = CreateFile(p.c_str(),
|
fh = CreateFile(p.c_str(),
|
||||||
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||||
|
@ -84,15 +72,19 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
if (fh == INVALID_HANDLE_VALUE) {
|
if (fh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(ec, "CreateFile");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||||
|
|
||||||
GetFileSizeEx(fh, &file_size);
|
|
||||||
|
|
||||||
if (length == -1)
|
if (length == -1) {
|
||||||
|
LARGE_INTEGER file_size;
|
||||||
|
GetFileSizeEx(fh, &file_size);
|
||||||
length = file_size.QuadPart;
|
length = file_size.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == 0) return;
|
||||||
|
|
||||||
DWORD protect = 0;
|
DWORD protect = 0;
|
||||||
DWORD access = 0;
|
DWORD access = 0;
|
||||||
|
@ -113,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);
|
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||||
if (mh == INVALID_HANDLE_VALUE) {
|
if (mh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(ec, "CreateFileMapping");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||||
|
@ -126,7 +118,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||||
length,
|
length,
|
||||||
nullptr);
|
nullptr);
|
||||||
if (!_data) {
|
if (!_data) {
|
||||||
throw_error();
|
return set_or_throw_error(ec, "MapViewOfFileEx");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,10 +141,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
const DWORD access = FILE_MAP_WRITE;
|
const DWORD access = FILE_MAP_WRITE;
|
||||||
|
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fh = CreateFile(p.c_str(),
|
fh = CreateFile(p.c_str(),
|
||||||
GENERIC_READ | GENERIC_WRITE,
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
@ -163,7 +152,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
if (fh == INVALID_HANDLE_VALUE) {
|
if (fh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(nullptr, "CreateFile");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||||
|
@ -171,11 +160,11 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
|
|
||||||
file_size.QuadPart = length;
|
file_size.QuadPart = length;
|
||||||
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN));
|
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);
|
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||||
if (mh == INVALID_HANDLE_VALUE) {
|
if (mh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(nullptr, "CreateFileMapping");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||||
|
@ -188,7 +177,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
if (!_data) {
|
if (!_data) {
|
||||||
throw_error();
|
return set_or_throw_error(nullptr, "MapViewOfFileEx");
|
||||||
}
|
}
|
||||||
|
|
||||||
_file_handle = fh_close.release();
|
_file_handle = fh_close.release();
|
||||||
|
@ -210,13 +199,10 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void throw_error() {
|
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||||
throw_error(errno);
|
set_or_throw_error(ec, errno, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_error(const std::string &what) {
|
|
||||||
throw_error(errno, what);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,16 +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 fd;
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
int oflags = 0;
|
int oflags = 0;
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (flags) {
|
switch (flags) {
|
||||||
case readonly:
|
case readonly:
|
||||||
|
@ -251,18 +236,25 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||||
|
|
||||||
fd = ::open(p.c_str(), oflags);
|
fd = ::open(p.c_str(), oflags);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
throw_error(errno);
|
return set_or_throw_error(ec, "open");
|
||||||
}
|
}
|
||||||
|
|
||||||
//defer([fd](){::close(fd); });
|
//defer([fd](){::close(fd); });
|
||||||
auto close_fd = make_unique_resource(fd, ::close);
|
auto close_fd = make_unique_resource(fd, ::close);
|
||||||
|
|
||||||
if (::fstat(fd, &st) < 0) {
|
|
||||||
throw_error(errno);
|
|
||||||
|
if (length == -1) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (::fstat(fd, &st) < 0) {
|
||||||
|
set_or_throw_error(ec, "stat");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
length = st.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length == 0) return;
|
||||||
if (length == -1) length = st.st_size;
|
|
||||||
|
|
||||||
_data = ::mmap(0, length,
|
_data = ::mmap(0, length,
|
||||||
flags == readonly ? PROT_READ : PROT_READ | PROT_WRITE,
|
flags == readonly ? PROT_READ : PROT_READ | PROT_WRITE,
|
||||||
|
@ -271,7 +263,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
||||||
|
|
||||||
if (_data == MAP_FAILED) {
|
if (_data == MAP_FAILED) {
|
||||||
_data = nullptr;
|
_data = nullptr;
|
||||||
throw_error(errno);
|
return set_or_throw_error(ec, "mmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
_fd = close_fd.release();
|
_fd = close_fd.release();
|
||||||
|
@ -284,15 +276,11 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
int fd;
|
int fd;
|
||||||
const size_t offset = 0;
|
const size_t offset = 0;
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||||
|
|
||||||
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
throw_error(errno);
|
return set_or_throw_error(nullptr, "open");
|
||||||
}
|
}
|
||||||
|
|
||||||
//defer([fd](){::close(fd); });
|
//defer([fd](){::close(fd); });
|
||||||
|
@ -301,7 +289,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
|
|
||||||
|
|
||||||
if (::ftruncate(fd, length) < 0) {
|
if (::ftruncate(fd, length) < 0) {
|
||||||
throw_error(errno);
|
return set_or_throw_error(nullptr, "ftruncate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -312,7 +300,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
||||||
|
|
||||||
if (_data == MAP_FAILED) {
|
if (_data == MAP_FAILED) {
|
||||||
_data = nullptr;
|
_data = nullptr;
|
||||||
throw_error(errno);
|
return set_or_throw_error(nullptr, "mmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
_fd = close_fd.release();
|
_fd = close_fd.release();
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
class mapped_file_base {
|
class mapped_file_base {
|
||||||
public:
|
public:
|
||||||
|
@ -40,7 +40,7 @@ protected:
|
||||||
|
|
||||||
void swap(mapped_file_base &rhs);
|
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 create(const path_type &p, size_t new_size); // always creates readwrite.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
@ -79,6 +79,22 @@ public:
|
||||||
open(p, flags, length, offset);
|
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(mapped_file &&);
|
||||||
mapped_file(const mapped_file &) = delete;
|
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) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
193
environment.cpp
193
environment.cpp
|
@ -2,6 +2,8 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -18,8 +20,51 @@ namespace {
|
||||||
if (s.size() == 1 && s == "0") return false;
|
if (s.size() == 1 && s == "0") return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
try {
|
||||||
|
int n = stoi(s);
|
||||||
|
return std::max(n, (int)0);
|
||||||
|
}
|
||||||
|
catch(std::exception e) {}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int to_pound_int(long n) { return std::max(n, (long)0); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 {
|
std::string Environment::get(const std::string & key) const {
|
||||||
auto iter = find(key);
|
auto iter = find(key);
|
||||||
if (iter == end()) return "";
|
if (iter == end()) return "";
|
||||||
|
@ -38,6 +83,7 @@ namespace {
|
||||||
return _table.find(k);
|
return _table.find(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Environment::set(const std::string &key, const std::string &value, bool exported) {
|
void Environment::set(const std::string &key, const std::string &value, bool exported) {
|
||||||
std::string k(key);
|
std::string k(key);
|
||||||
lowercase(k);
|
lowercase(k);
|
||||||
|
@ -45,10 +91,81 @@ namespace {
|
||||||
if (k == "echo") _echo = tf(value);
|
if (k == "echo") _echo = tf(value);
|
||||||
if (k == "exit") _exit = tf(value);
|
if (k == "exit") _exit = tf(value);
|
||||||
if (k == "test") _test = tf(value);
|
if (k == "test") _test = tf(value);
|
||||||
|
if (k == "#") _pound = to_pound_int(value);
|
||||||
|
|
||||||
// don't need to check {status} because that will be clobbered
|
// don't need to check {status} because that will be clobbered
|
||||||
// by the return value.
|
// by the return value.
|
||||||
|
set_common(k, value, exported);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Environment::set(const std::string &key, long value, bool exported) {
|
||||||
|
std::string k(key);
|
||||||
|
lowercase(k);
|
||||||
|
|
||||||
|
if (k == "echo") _echo = tf(value);
|
||||||
|
if (k == "exit") _exit = tf(value);
|
||||||
|
if (k == "test") _test = tf(value);
|
||||||
|
if (k == "#") _pound = to_pound_int(value);
|
||||||
|
|
||||||
|
// don't need to check {status} because that will be clobbered
|
||||||
|
// by the return value.
|
||||||
|
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() - 1;
|
||||||
|
set_common("#", std::to_string(_pound), false);
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
for (const auto &s : argv) {
|
||||||
|
set_common(std::to_string(n++), s, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameters, "parameters" ...
|
||||||
|
std::string p;
|
||||||
|
for (const auto &s : argv) {
|
||||||
|
p.push_back('"');
|
||||||
|
p += s;
|
||||||
|
p.push_back('"');
|
||||||
|
p.push_back(' ');
|
||||||
|
}
|
||||||
|
p.pop_back();
|
||||||
|
set_common("\"parameters\"", p, false);
|
||||||
|
p.clear();
|
||||||
|
|
||||||
|
for (const auto &s : argv) {
|
||||||
|
p += s;
|
||||||
|
p.push_back(' ');
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Environment::set_common(const std::string &k, const std::string &value, bool exported)
|
||||||
|
{
|
||||||
EnvironmentEntry v(value, exported);
|
EnvironmentEntry v(value, exported);
|
||||||
|
|
||||||
auto iter = _table.find(k);
|
auto iter = _table.find(k);
|
||||||
|
@ -59,15 +176,20 @@ namespace {
|
||||||
// if previously exported, keep exported.
|
// if previously exported, keep exported.
|
||||||
if (iter->second) v = true;
|
if (iter->second) v = true;
|
||||||
iter->second = std::move(v);
|
iter->second = std::move(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Environment::unset(const std::string &key) {
|
void Environment::unset(const std::string &key) {
|
||||||
std::string k(key);
|
std::string k(key);
|
||||||
lowercase(k);
|
lowercase(k);
|
||||||
if (k == "echo") _echo = false;
|
if (k == "echo") _echo = false;
|
||||||
if (k == "exit") _exit = false;
|
if (k == "exit") _exit = false;
|
||||||
if (k == "test") _test = false;
|
if (k == "test") _test = false;
|
||||||
|
if (k == "#") _pound = 0;
|
||||||
_table.erase(k);
|
_table.erase(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,18 +220,81 @@ namespace {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Environment::echo(const char *fmt, ...) {
|
void Environment::echo(const char *fmt, ...) const {
|
||||||
if (_echo && !_startup) {
|
if (_echo && !_startup) {
|
||||||
for (unsigned i = 0; i < _indent; ++i) {
|
for (int i = 0; i <= _indent; ++i) {
|
||||||
fputc(' ', stderr);
|
fputc(' ', stderr);
|
||||||
fputc(' ', stderr);
|
fputc(' ', stderr);
|
||||||
}
|
}
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
va_end(ap);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Environment::rebuild_aliases() {
|
||||||
|
std::string as;
|
||||||
|
for (const auto &p : _alias_table) {
|
||||||
|
as += p.first;
|
||||||
|
as.push_back(',');
|
||||||
|
}
|
||||||
|
as.pop_back();
|
||||||
|
set_common("aliases", as, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Environment::remove_alias() {
|
||||||
|
_alias_table.clear();
|
||||||
|
set_common("aliases", "", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Environment::remove_alias(const std::string &name) {
|
||||||
|
|
||||||
|
std::string k(name);
|
||||||
|
lowercase(k);
|
||||||
|
|
||||||
|
auto iter = std::remove_if(_alias_table.begin(), _alias_table.end(), [&k](const auto &p){
|
||||||
|
return k == p.first;
|
||||||
|
});
|
||||||
|
_alias_table.erase(iter, _alias_table.end());
|
||||||
|
rebuild_aliases();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &Environment::find_alias(const std::string &name) const {
|
||||||
|
|
||||||
|
std::string k(name);
|
||||||
|
lowercase(k);
|
||||||
|
|
||||||
|
auto iter = std::find_if(_alias_table.begin(), _alias_table.end(), [&k](const auto &p){
|
||||||
|
return k == p.first;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (iter == _alias_table.end()) {
|
||||||
|
static std::string empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Environment::add_alias(std::string &&name, std::string &&value) {
|
||||||
|
|
||||||
|
lowercase(name);
|
||||||
|
|
||||||
|
auto iter = std::find_if(_alias_table.begin(), _alias_table.end(), [&name](const auto &p){
|
||||||
|
return name == p.first;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (iter == _alias_table.end()) {
|
||||||
|
_alias_table.emplace_back(std::make_pair(std::move(name), std::move(value)));
|
||||||
|
} else {
|
||||||
|
iter->second = std::move(value);
|
||||||
|
}
|
||||||
|
rebuild_aliases();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
#ifndef __environment_h__
|
#ifndef __environment_h__
|
||||||
#define __environment_h__
|
#define __environment_h__
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <new>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <new>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,32 +48,40 @@ private:
|
||||||
class Environment {
|
class Environment {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::unordered_map<std::string, EnvironmentEntry> mapped_type;
|
typedef std::map<std::string, EnvironmentEntry> mapped_type;
|
||||||
typedef mapped_type::iterator iterator;
|
typedef mapped_type::iterator iterator;
|
||||||
typedef mapped_type::const_iterator const_iterator;
|
typedef mapped_type::const_iterator const_iterator;
|
||||||
|
|
||||||
//const EnvironmentEntry & lookup(const std::string &s);
|
|
||||||
|
typedef std::vector<std::pair<std::string, std::string>> alias_table_type;
|
||||||
|
typedef alias_table_type::const_iterator const_alias_iterator;
|
||||||
|
|
||||||
|
|
||||||
|
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, const std::string &value, bool exported = false);
|
||||||
|
void set(const std::string &k, long l, bool exported = false);
|
||||||
void unset(const std::string &k);
|
void unset(const std::string &k);
|
||||||
void unset();
|
void unset();
|
||||||
|
|
||||||
std::string get(const std::string &k) const;
|
std::string get(const std::string &k) const;
|
||||||
|
|
||||||
constexpr bool echo() const noexcept { return _echo; }
|
bool echo() const noexcept { return _echo; }
|
||||||
constexpr bool test() const noexcept { return _test; }
|
bool test() const noexcept { return _test; }
|
||||||
constexpr bool exit() const noexcept { return _exit; }
|
bool exit() const noexcept { return _exit; }
|
||||||
constexpr int status() const noexcept { return _status; }
|
int status() const noexcept { return _status; }
|
||||||
|
int pound() const noexcept { return _pound; }
|
||||||
|
|
||||||
int status(int i, bool throw_up = true);
|
int status(int i, bool throw_up = true);
|
||||||
int status(int i, const std::nothrow_t &);
|
int status(int i, const std::nothrow_t &);
|
||||||
|
|
||||||
constexpr bool startup() const noexcept { return _startup; }
|
bool startup() const noexcept { return _startup; }
|
||||||
constexpr void startup(bool tf) noexcept { _startup = tf; }
|
void startup(bool tf) noexcept { _startup = tf; }
|
||||||
|
|
||||||
|
|
||||||
constexpr bool passthrough() const noexcept { return _passthrough; }
|
|
||||||
constexpr void passthrough(bool tf) noexcept { _passthrough = tf; }
|
|
||||||
|
|
||||||
template<class FX>
|
template<class FX>
|
||||||
void foreach(FX && fx) { for (const auto &kv : _table) { fx(kv.first, kv.second); }}
|
void foreach(FX && fx) { for (const auto &kv : _table) { fx(kv.first, kv.second); }}
|
||||||
|
@ -89,7 +99,7 @@ public:
|
||||||
const_iterator find( const std::string & key ) const;
|
const_iterator find( const std::string & key ) const;
|
||||||
|
|
||||||
|
|
||||||
void echo(const char *format, ...);
|
void echo(const char *format, ...) const;
|
||||||
|
|
||||||
template<class FX>
|
template<class FX>
|
||||||
void indent_and(FX &&fx) {
|
void indent_and(FX &&fx) {
|
||||||
|
@ -98,34 +108,48 @@ public:
|
||||||
catch (...) { _indent = i; throw; }
|
catch (...) { _indent = i; throw; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class FX>
|
||||||
|
void loop_indent_and(FX &&fx) {
|
||||||
|
int i = _indent++;
|
||||||
|
int j = _loop++;
|
||||||
|
try { fx(); _indent = i; _loop = j; }
|
||||||
|
catch (...) { _indent = i; _loop = j; throw; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loop() const noexcept { return _loop; }
|
||||||
|
|
||||||
|
const alias_table_type &aliases() const { return _alias_table; }
|
||||||
|
|
||||||
|
void add_alias(std::string &&name, std::string &&value);
|
||||||
|
const std::string &find_alias(const std::string &s) const;
|
||||||
|
|
||||||
|
void remove_alias(const std::string &name);
|
||||||
|
void remove_alias();
|
||||||
|
|
||||||
|
const_alias_iterator alias_begin() const { return _alias_table.begin(); }
|
||||||
|
const_alias_iterator alias_end() const { return _alias_table.end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// magic variables.
|
// magic variables.
|
||||||
|
|
||||||
friend class indent_helper;
|
|
||||||
|
|
||||||
int _indent = 0;
|
int _indent = 0;
|
||||||
|
int _loop = 0;
|
||||||
|
|
||||||
bool _exit = false;
|
bool _exit = false;
|
||||||
bool _test = false;
|
bool _test = false;
|
||||||
|
|
||||||
bool _echo = false;
|
bool _echo = false;
|
||||||
int _status = 0;
|
int _status = 0;
|
||||||
|
int _pound = 0;
|
||||||
bool _startup = false;
|
bool _startup = false;
|
||||||
bool _passthrough = false;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, EnvironmentEntry> _table;
|
void set_common(const std::string &, const std::string &, bool);
|
||||||
|
void rebuild_aliases();
|
||||||
|
|
||||||
|
mapped_type _table;
|
||||||
|
|
||||||
|
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
|
#endif
|
||||||
|
|
96
error.h
96
error.h
|
@ -4,14 +4,100 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
class execution_of_input_terminated : public std::runtime_error {
|
class mpw_error : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
execution_of_input_terminated(int status = -9) :
|
mpw_error(int status, const std::string &s) : std::runtime_error(s), _status(status)
|
||||||
std::runtime_error("MPW Shell - Execution of input Terminated."), _status(status)
|
|
||||||
{}
|
{}
|
||||||
constexpr int status() const noexcept { return _status; }
|
|
||||||
|
mpw_error(int status, const char *s) : std::runtime_error(s), _status(status)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int status() const noexcept { return _status; }
|
||||||
private:
|
private:
|
||||||
int _status;
|
int _status;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
class execution_of_input_terminated : public mpw_error {
|
||||||
|
public:
|
||||||
|
execution_of_input_terminated(int status = -9) :
|
||||||
|
mpw_error(status, "MPW Shell - Execution of input Terminated.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class break_error : public mpw_error {
|
||||||
|
public:
|
||||||
|
break_error(int status = -3) :
|
||||||
|
mpw_error(status, "MPW Shell - Break must be within for or loop.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class continue_error : public mpw_error {
|
||||||
|
public:
|
||||||
|
continue_error(int status = -3) :
|
||||||
|
mpw_error(status, "MPW Shell - Continue must be within for or loop.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class estring_error: public mpw_error {
|
||||||
|
public:
|
||||||
|
estring_error(int status = -3) :
|
||||||
|
mpw_error(status, "MPW Shell - `s must occur in pairs.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class vstring_error: public mpw_error {
|
||||||
|
public:
|
||||||
|
vstring_error(int status = -3) :
|
||||||
|
mpw_error(status, "MPW Shell - {s must occur in pairs.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class sstring_error: public mpw_error {
|
||||||
|
public:
|
||||||
|
sstring_error(int status = -3) :
|
||||||
|
mpw_error(status, "MPW Shell - 's must occur in pairs.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class dstring_error: public mpw_error {
|
||||||
|
public:
|
||||||
|
dstring_error(int status = -3) :
|
||||||
|
mpw_error(status, "MPW Shell - \"s must occur in pairs.")
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
|
74
etc/MPW Shell.terminal
Normal file
74
etc/MPW Shell.terminal
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CharacterEncoding</key>
|
||||||
|
<integer>30</integer>
|
||||||
|
<key>CommandString</key>
|
||||||
|
<string>/usr/local/bin/mpw-shell</string>
|
||||||
|
<key>CursorBlink</key>
|
||||||
|
<true/>
|
||||||
|
<key>CursorType</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>Font</key>
|
||||||
|
<data>
|
||||||
|
YnBsaXN0MDDUAQIDBAUGGBlYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
|
||||||
|
AAGGoKQHCBESVSRudWxs1AkKCwwNDg8QVk5TU2l6ZVhOU2ZGbGFnc1ZOU05hbWVWJGNs
|
||||||
|
YXNzI0AsAAAAAAAAEBCAAoADVk1vbmFjb9ITFBUWWiRjbGFzc25hbWVYJGNsYXNzZXNW
|
||||||
|
TlNGb250ohUXWE5TT2JqZWN0XxAPTlNLZXllZEFyY2hpdmVy0RobVHJvb3SAAQgRGiMt
|
||||||
|
Mjc8QktSW2JpcnR2eH+Ej5ifoqu9wMUAAAAAAAABAQAAAAAAAAAcAAAAAAAAAAAAAAAA
|
||||||
|
AAAAxw==
|
||||||
|
</data>
|
||||||
|
<key>FontAntialias</key>
|
||||||
|
<true/>
|
||||||
|
<key>ProfileCurrentVersion</key>
|
||||||
|
<real>2.04</real>
|
||||||
|
<key>RunCommandAsShell</key>
|
||||||
|
<true/>
|
||||||
|
<key>ShowRepresentedURLInTitle</key>
|
||||||
|
<false/>
|
||||||
|
<key>ShowRepresentedURLPathInTitle</key>
|
||||||
|
<false/>
|
||||||
|
<key>ShowShellCommandInTitle</key>
|
||||||
|
<false/>
|
||||||
|
<key>TextBoldColor</key>
|
||||||
|
<data>
|
||||||
|
YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
|
||||||
|
AAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NGMCAw
|
||||||
|
IDAAEAGAAtIQERITWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xvcqISFFhOU09iamVj
|
||||||
|
dF8QD05TS2V5ZWRBcmNoaXZlctEXGFRyb290gAEIERojLTI3O0FITltiaWttcn2GjpGa
|
||||||
|
rK+0AAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAALY=
|
||||||
|
</data>
|
||||||
|
<key>TextColor</key>
|
||||||
|
<data>
|
||||||
|
YnBsaXN0MDDUAQIDBAUGFRZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
|
||||||
|
AAGGoKMHCA9VJG51bGzTCQoLDA0OVU5TUkdCXE5TQ29sb3JTcGFjZVYkY2xhc3NGMCAw
|
||||||
|
IDEAEAGAAtIQERITWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xvcqISFFhOU09iamVj
|
||||||
|
dF8QD05TS2V5ZWRBcmNoaXZlctEXGFRyb290gAEIERojLTI3O0FITltiaWttcn2GjpGa
|
||||||
|
rK+0AAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAALY=
|
||||||
|
</data>
|
||||||
|
<key>columnCount</key>
|
||||||
|
<integer>100</integer>
|
||||||
|
<key>name</key>
|
||||||
|
<string>MPW Shell</string>
|
||||||
|
<key>noWarnProcesses</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>ProcessName</key>
|
||||||
|
<string>screen</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>ProcessName</key>
|
||||||
|
<string>tmux</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>ProcessName</key>
|
||||||
|
<string>mpw-shell</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>rowCount</key>
|
||||||
|
<integer>50</integer>
|
||||||
|
<key>type</key>
|
||||||
|
<string>Window Settings</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
57
etc/Startup
Normal file
57
etc/Startup
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# MPW Shell start up script.
|
||||||
|
# only built-in commands are executed.
|
||||||
|
#
|
||||||
|
# {MPW} is pre-defined.
|
||||||
|
|
||||||
|
if {MPWVersion}==""
|
||||||
|
set -e MPWVersion 3.5
|
||||||
|
end
|
||||||
|
|
||||||
|
set -e ShellDirectory "{MPW}{MPWVersion}:"
|
||||||
|
set -e Commands "{ShellDirectory}Tools:,{MPW}Tools:,:"
|
||||||
|
|
||||||
|
set -e SysTempFolder "/tmp/"
|
||||||
|
set -e TempFolder "/tmp/"
|
||||||
|
|
||||||
|
# MPW IIgs
|
||||||
|
set -e AIIGSIncludes "{MPW}Interfaces:AIIGSIncludes:"
|
||||||
|
set -e RIIGSIncludes "{MPW}Interfaces:RIIGSIncludes:"
|
||||||
|
set -e CIIGSIncludes "{MPW}Interfaces:CIIGSIncludes:"
|
||||||
|
set -e CIIGSLibraries "{MPW}Libraries:CIIGSLibraries:"
|
||||||
|
set -e PIIGSIncludes "{MPW}Interfaces:PIIGSIncludes:"
|
||||||
|
set -e PIIGSLibraries "{MPW}Libraries:PIIGSLibraries:"
|
||||||
|
|
||||||
|
# MPW Macintosh compilers
|
||||||
|
set -e SCIncludes "{ShellDirectory}:Interfaces:CIncludes:"
|
||||||
|
set -e CIncludes "{ShellDirectory}:Interfaces:CIncludes:"
|
||||||
|
set -e AIncludes "{ShellDirectory}Interfaces:AIncludes:"
|
||||||
|
set -e RIncludes "{ShellDirectory}Interfaces:RIncludes:"
|
||||||
|
set -e PInterfaces "{ShellDirectory}Interfaces:PInterfaces:"
|
||||||
|
|
||||||
|
set -e Libraries "{ShellDirectory}Libraries:Libraries:"
|
||||||
|
set -e PLibraries "{ShellDirectory}Libraries:PLibraries:"
|
||||||
|
set -e CLibraries "{ShellDirectory}Libraries:CLibraries:"
|
||||||
|
|
||||||
|
set -e PPCLibraries "{ShellDirectory}Libraries:PPCLibraries:"
|
||||||
|
set -e SharedLibraries "{ShellDirectory}Libraries:SharedLibraries:"
|
||||||
|
set -e CFM68KLibraries "{ShellDirectory}Libraries:CFM68KLibraries:"
|
||||||
|
|
||||||
|
#MetroWerks
|
||||||
|
set -e MW68KLibraries "{MPW}Libraries:MW68KLibraries:"
|
||||||
|
set -e MWPPCLibraries "{MPW}Libraries:MWPPCLibraries:"
|
||||||
|
|
||||||
|
set -e CWCIncludes "{MPW}Interfaces:CWCIncludes:"
|
||||||
|
set -e CWANSIIncludes "{MPW}Interfaces:CWANSIIncludes:"
|
||||||
|
set -e MWCIncludes "{CIncludes}"
|
||||||
|
|
||||||
|
|
||||||
|
#Newton Tools
|
||||||
|
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
|
||||||
|
|
4
fdset.h
4
fdset.h
|
@ -132,6 +132,10 @@ class fdset {
|
||||||
return fdmask(_fds);
|
return fdmask(_fds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void swap_in_out() {
|
||||||
|
std::swap(_fds[0], _fds[1]);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
|
|
11
lemon_base.h
11
lemon_base.h
|
@ -9,14 +9,13 @@ public:
|
||||||
|
|
||||||
virtual ~lemon_base() = default;
|
virtual ~lemon_base() = default;
|
||||||
|
|
||||||
#if 0
|
|
||||||
virtual typename std::enable_if<std::is_copy_constructible<TokenType>::value, void>::type
|
|
||||||
parse(int yymajor, const TokenType &yyminor) = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//virtual typename std::enable_if<std::is_move_constructible<TokenType>::value, void>::type
|
//virtual typename std::enable_if<std::is_move_constructible<TokenType>::value, void>::type
|
||||||
virtual void parse(int yymajor, TokenType &&yyminor) = 0;
|
virtual void parse(int yymajor, TokenType &&yyminor) = 0;
|
||||||
virtual void trace(FILE *, const char *) = 0;
|
|
||||||
|
virtual void trace(FILE *, const char *) {}
|
||||||
|
|
||||||
|
virtual bool will_accept() const = 0;
|
||||||
|
virtual void reset() {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void parse_accept() {}
|
virtual void parse_accept() {}
|
||||||
|
|
1065
lempar.cxx
1065
lempar.cxx
File diff suppressed because it is too large
Load Diff
163
macroman.cpp
Normal file
163
macroman.cpp
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
|
||||||
|
struct mu_pair {
|
||||||
|
uint16_t unicode;
|
||||||
|
uint8_t macroman;
|
||||||
|
|
||||||
|
constexpr bool operator<(const mu_pair &rhs) const noexcept {
|
||||||
|
return unicode < rhs.unicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const mu_pair &rhs) const noexcept {
|
||||||
|
return unicode == rhs.unicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator<(uint16_t c) const noexcept {
|
||||||
|
return unicode < c;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(uint16_t c) const noexcept {
|
||||||
|
return unicode == c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t unicode_to_macroman(uint16_t c){
|
||||||
|
|
||||||
|
|
||||||
|
static std::array< mu_pair, 0x80> table = {{
|
||||||
|
#undef _
|
||||||
|
#define _(macroman, unicode, comment) mu_pair{ unicode, macroman } ,
|
||||||
|
#include "macroman.x"
|
||||||
|
#undef _
|
||||||
|
}};
|
||||||
|
|
||||||
|
static bool init = false;
|
||||||
|
|
||||||
|
if (c < 0x80) return c;
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
init = true;
|
||||||
|
std::sort(table.begin(), table.end());
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = std::lower_bound(table.begin(), table.end(), c);
|
||||||
|
if (iter != table.end() && iter->unicode == c) return iter->macroman;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t macroman_to_unicode(uint8_t c) {
|
||||||
|
|
||||||
|
static std::array<uint16_t, 0x80> table = {{
|
||||||
|
#undef _
|
||||||
|
#define _(macroman,unicode,comment) unicode ,
|
||||||
|
#include "macroman.x"
|
||||||
|
#undef _
|
||||||
|
}};
|
||||||
|
|
||||||
|
if (c < 0x80) return c;
|
||||||
|
return table[c - 0x80];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::string utf8_to_macroman(const std::string &s) {
|
||||||
|
|
||||||
|
|
||||||
|
if (std::find_if(s.begin(), s.end(), [](unsigned char c){ return c & 0x80; }) == s.end())
|
||||||
|
return s;
|
||||||
|
|
||||||
|
std::string rv;
|
||||||
|
rv.reserve(s.size());
|
||||||
|
|
||||||
|
unsigned cs = 0;
|
||||||
|
uint16_t tmp = 0;
|
||||||
|
|
||||||
|
for (unsigned char c : s) {
|
||||||
|
switch(cs) {
|
||||||
|
case 0:
|
||||||
|
if (c <= 0x7f) {
|
||||||
|
rv.push_back(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((c & 0b11100000) == 0b11000000) {
|
||||||
|
(tmp = c & 0b00011111);
|
||||||
|
cs = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((c & 0b11110000) == 0b11100000) {
|
||||||
|
(tmp = c & 0b00001111);
|
||||||
|
cs = 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((c & 0b11111000) == 0b11110000) {
|
||||||
|
(tmp = c & 0b00000111);
|
||||||
|
cs = 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// not utf8...
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
if ((c & 0b11000000) != 0b10000000) {
|
||||||
|
//not utf8...
|
||||||
|
}
|
||||||
|
tmp = (tmp << 6) + (c & 0b00111111);
|
||||||
|
if (--cs == 0) {
|
||||||
|
c = unicode_to_macroman(tmp);
|
||||||
|
if (c) rv.push_back(c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string macroman_to_utf8(const std::string &s) {
|
||||||
|
|
||||||
|
if (std::find_if(s.begin(), s.end(), [](unsigned char c){ return c & 0x80; }) == s.end())
|
||||||
|
return s;
|
||||||
|
|
||||||
|
std::string rv;
|
||||||
|
rv.reserve(s.size());
|
||||||
|
|
||||||
|
for (uint8_t c : s) {
|
||||||
|
if (c <= 0x7f) { rv.push_back(c); continue; }
|
||||||
|
|
||||||
|
uint16_t tmp = macroman_to_unicode(c);
|
||||||
|
|
||||||
|
if (tmp <= 0x7f) {
|
||||||
|
rv.push_back(tmp);
|
||||||
|
} else if (tmp <= 0x07ff) {
|
||||||
|
uint8_t a,b;
|
||||||
|
b = tmp & 0b00111111; tmp >>= 6;
|
||||||
|
a = tmp;
|
||||||
|
rv.push_back(0b11000000 | a);
|
||||||
|
rv.push_back(0b10000000 | b);
|
||||||
|
} else { // tmp <= 0xffff
|
||||||
|
uint8_t a,b,c;
|
||||||
|
c = tmp & 0b00111111; tmp >>= 6;
|
||||||
|
b = tmp & 0b00111111; tmp >>= 6;
|
||||||
|
a = tmp;
|
||||||
|
rv.push_back(0b11100000 | a);
|
||||||
|
rv.push_back(0b10000000 | b);
|
||||||
|
rv.push_back(0b10000000 | c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
8
macroman.h
Normal file
8
macroman.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __macroman_h__
|
||||||
|
#define __macroman_h__
|
||||||
|
|
||||||
|
std::string utf8_to_macroman(const std::string &s);
|
||||||
|
std::string macroman_to_utf8(const std::string &s);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
130
macroman.x
Normal file
130
macroman.x
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
|
||||||
|
// http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT
|
||||||
|
_(0x80, 0x00C4, "# LATIN CAPITAL LETTER A WITH DIAERESIS")
|
||||||
|
_(0x81, 0x00C5, "# LATIN CAPITAL LETTER A WITH RING ABOVE")
|
||||||
|
_(0x82, 0x00C7, "# LATIN CAPITAL LETTER C WITH CEDILLA")
|
||||||
|
_(0x83, 0x00C9, "# LATIN CAPITAL LETTER E WITH ACUTE")
|
||||||
|
_(0x84, 0x00D1, "# LATIN CAPITAL LETTER N WITH TILDE")
|
||||||
|
_(0x85, 0x00D6, "# LATIN CAPITAL LETTER O WITH DIAERESIS")
|
||||||
|
_(0x86, 0x00DC, "# LATIN CAPITAL LETTER U WITH DIAERESIS")
|
||||||
|
_(0x87, 0x00E1, "# LATIN SMALL LETTER A WITH ACUTE")
|
||||||
|
_(0x88, 0x00E0, "# LATIN SMALL LETTER A WITH GRAVE")
|
||||||
|
_(0x89, 0x00E2, "# LATIN SMALL LETTER A WITH CIRCUMFLEX")
|
||||||
|
_(0x8A, 0x00E4, "# LATIN SMALL LETTER A WITH DIAERESIS")
|
||||||
|
_(0x8B, 0x00E3, "# LATIN SMALL LETTER A WITH TILDE")
|
||||||
|
_(0x8C, 0x00E5, "# LATIN SMALL LETTER A WITH RING ABOVE")
|
||||||
|
_(0x8D, 0x00E7, "# LATIN SMALL LETTER C WITH CEDILLA")
|
||||||
|
_(0x8E, 0x00E9, "# LATIN SMALL LETTER E WITH ACUTE")
|
||||||
|
_(0x8F, 0x00E8, "# LATIN SMALL LETTER E WITH GRAVE")
|
||||||
|
_(0x90, 0x00EA, "# LATIN SMALL LETTER E WITH CIRCUMFLEX")
|
||||||
|
_(0x91, 0x00EB, "# LATIN SMALL LETTER E WITH DIAERESIS")
|
||||||
|
_(0x92, 0x00ED, "# LATIN SMALL LETTER I WITH ACUTE")
|
||||||
|
_(0x93, 0x00EC, "# LATIN SMALL LETTER I WITH GRAVE")
|
||||||
|
_(0x94, 0x00EE, "# LATIN SMALL LETTER I WITH CIRCUMFLEX")
|
||||||
|
_(0x95, 0x00EF, "# LATIN SMALL LETTER I WITH DIAERESIS")
|
||||||
|
_(0x96, 0x00F1, "# LATIN SMALL LETTER N WITH TILDE")
|
||||||
|
_(0x97, 0x00F3, "# LATIN SMALL LETTER O WITH ACUTE")
|
||||||
|
_(0x98, 0x00F2, "# LATIN SMALL LETTER O WITH GRAVE")
|
||||||
|
_(0x99, 0x00F4, "# LATIN SMALL LETTER O WITH CIRCUMFLEX")
|
||||||
|
_(0x9A, 0x00F6, "# LATIN SMALL LETTER O WITH DIAERESIS")
|
||||||
|
_(0x9B, 0x00F5, "# LATIN SMALL LETTER O WITH TILDE")
|
||||||
|
_(0x9C, 0x00FA, "# LATIN SMALL LETTER U WITH ACUTE")
|
||||||
|
_(0x9D, 0x00F9, "# LATIN SMALL LETTER U WITH GRAVE")
|
||||||
|
_(0x9E, 0x00FB, "# LATIN SMALL LETTER U WITH CIRCUMFLEX")
|
||||||
|
_(0x9F, 0x00FC, "# LATIN SMALL LETTER U WITH DIAERESIS")
|
||||||
|
_(0xA0, 0x2020, "# DAGGER")
|
||||||
|
_(0xA1, 0x00B0, "# DEGREE SIGN")
|
||||||
|
_(0xA2, 0x00A2, "# CENT SIGN")
|
||||||
|
_(0xA3, 0x00A3, "# POUND SIGN")
|
||||||
|
_(0xA4, 0x00A7, "# SECTION SIGN")
|
||||||
|
_(0xA5, 0x2022, "# BULLET")
|
||||||
|
_(0xA6, 0x00B6, "# PILCROW SIGN")
|
||||||
|
_(0xA7, 0x00DF, "# LATIN SMALL LETTER SHARP S")
|
||||||
|
_(0xA8, 0x00AE, "# REGISTERED SIGN")
|
||||||
|
_(0xA9, 0x00A9, "# COPYRIGHT SIGN")
|
||||||
|
_(0xAA, 0x2122, "# TRADE MARK SIGN")
|
||||||
|
_(0xAB, 0x00B4, "# ACUTE ACCENT")
|
||||||
|
_(0xAC, 0x00A8, "# DIAERESIS")
|
||||||
|
_(0xAD, 0x2260, "# NOT EQUAL TO")
|
||||||
|
_(0xAE, 0x00C6, "# LATIN CAPITAL LETTER AE")
|
||||||
|
_(0xAF, 0x00D8, "# LATIN CAPITAL LETTER O WITH STROKE")
|
||||||
|
_(0xB0, 0x221E, "# INFINITY")
|
||||||
|
_(0xB1, 0x00B1, "# PLUS-MINUS SIGN")
|
||||||
|
_(0xB2, 0x2264, "# LESS-THAN OR EQUAL TO")
|
||||||
|
_(0xB3, 0x2265, "# GREATER-THAN OR EQUAL TO")
|
||||||
|
_(0xB4, 0x00A5, "# YEN SIGN")
|
||||||
|
_(0xB5, 0x00B5, "# MICRO SIGN")
|
||||||
|
_(0xB6, 0x2202, "# PARTIAL DIFFERENTIAL")
|
||||||
|
_(0xB7, 0x2211, "# N-ARY SUMMATION")
|
||||||
|
_(0xB8, 0x220F, "# N-ARY PRODUCT")
|
||||||
|
_(0xB9, 0x03C0, "# GREEK SMALL LETTER PI")
|
||||||
|
_(0xBA, 0x222B, "# INTEGRAL")
|
||||||
|
_(0xBB, 0x00AA, "# FEMININE ORDINAL INDICATOR")
|
||||||
|
_(0xBC, 0x00BA, "# MASCULINE ORDINAL INDICATOR")
|
||||||
|
_(0xBD, 0x03A9, "# GREEK CAPITAL LETTER OMEGA")
|
||||||
|
_(0xBE, 0x00E6, "# LATIN SMALL LETTER AE")
|
||||||
|
_(0xBF, 0x00F8, "# LATIN SMALL LETTER O WITH STROKE")
|
||||||
|
_(0xC0, 0x00BF, "# INVERTED QUESTION MARK")
|
||||||
|
_(0xC1, 0x00A1, "# INVERTED EXCLAMATION MARK")
|
||||||
|
_(0xC2, 0x00AC, "# NOT SIGN")
|
||||||
|
_(0xC3, 0x221A, "# SQUARE ROOT")
|
||||||
|
_(0xC4, 0x0192, "# LATIN SMALL LETTER F WITH HOOK")
|
||||||
|
_(0xC5, 0x2248, "# ALMOST EQUAL TO")
|
||||||
|
_(0xC6, 0x2206, "# INCREMENT")
|
||||||
|
_(0xC7, 0x00AB, "# LEFT-POINTING DOUBLE ANGLE QUOTATION MARK")
|
||||||
|
_(0xC8, 0x00BB, "# RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK")
|
||||||
|
_(0xC9, 0x2026, "# HORIZONTAL ELLIPSIS")
|
||||||
|
_(0xCA, 0x00A0, "# NO-BREAK SPACE")
|
||||||
|
_(0xCB, 0x00C0, "# LATIN CAPITAL LETTER A WITH GRAVE")
|
||||||
|
_(0xCC, 0x00C3, "# LATIN CAPITAL LETTER A WITH TILDE")
|
||||||
|
_(0xCD, 0x00D5, "# LATIN CAPITAL LETTER O WITH TILDE")
|
||||||
|
_(0xCE, 0x0152, "# LATIN CAPITAL LIGATURE OE")
|
||||||
|
_(0xCF, 0x0153, "# LATIN SMALL LIGATURE OE")
|
||||||
|
_(0xD0, 0x2013, "# EN DASH")
|
||||||
|
_(0xD1, 0x2014, "# EM DASH")
|
||||||
|
_(0xD2, 0x201C, "# LEFT DOUBLE QUOTATION MARK")
|
||||||
|
_(0xD3, 0x201D, "# RIGHT DOUBLE QUOTATION MARK")
|
||||||
|
_(0xD4, 0x2018, "# LEFT SINGLE QUOTATION MARK")
|
||||||
|
_(0xD5, 0x2019, "# RIGHT SINGLE QUOTATION MARK")
|
||||||
|
_(0xD6, 0x00F7, "# DIVISION SIGN")
|
||||||
|
_(0xD7, 0x25CA, "# LOZENGE")
|
||||||
|
_(0xD8, 0x00FF, "# LATIN SMALL LETTER Y WITH DIAERESIS")
|
||||||
|
_(0xD9, 0x0178, "# LATIN CAPITAL LETTER Y WITH DIAERESIS")
|
||||||
|
_(0xDA, 0x2044, "# FRACTION SLASH")
|
||||||
|
_(0xDB, 0x20AC, "# EURO SIGN")
|
||||||
|
_(0xDC, 0x2039, "# SINGLE LEFT-POINTING ANGLE QUOTATION MARK")
|
||||||
|
_(0xDD, 0x203A, "# SINGLE RIGHT-POINTING ANGLE QUOTATION MARK")
|
||||||
|
_(0xDE, 0xFB01, "# LATIN SMALL LIGATURE FI")
|
||||||
|
_(0xDF, 0xFB02, "# LATIN SMALL LIGATURE FL")
|
||||||
|
_(0xE0, 0x2021, "# DOUBLE DAGGER")
|
||||||
|
_(0xE1, 0x00B7, "# MIDDLE DOT")
|
||||||
|
_(0xE2, 0x201A, "# SINGLE LOW-9 QUOTATION MARK")
|
||||||
|
_(0xE3, 0x201E, "# DOUBLE LOW-9 QUOTATION MARK")
|
||||||
|
_(0xE4, 0x2030, "# PER MILLE SIGN")
|
||||||
|
_(0xE5, 0x00C2, "# LATIN CAPITAL LETTER A WITH CIRCUMFLEX")
|
||||||
|
_(0xE6, 0x00CA, "# LATIN CAPITAL LETTER E WITH CIRCUMFLEX")
|
||||||
|
_(0xE7, 0x00C1, "# LATIN CAPITAL LETTER A WITH ACUTE")
|
||||||
|
_(0xE8, 0x00CB, "# LATIN CAPITAL LETTER E WITH DIAERESIS")
|
||||||
|
_(0xE9, 0x00C8, "# LATIN CAPITAL LETTER E WITH GRAVE")
|
||||||
|
_(0xEA, 0x00CD, "# LATIN CAPITAL LETTER I WITH ACUTE")
|
||||||
|
_(0xEB, 0x00CE, "# LATIN CAPITAL LETTER I WITH CIRCUMFLEX")
|
||||||
|
_(0xEC, 0x00CF, "# LATIN CAPITAL LETTER I WITH DIAERESIS")
|
||||||
|
_(0xED, 0x00CC, "# LATIN CAPITAL LETTER I WITH GRAVE")
|
||||||
|
_(0xEE, 0x00D3, "# LATIN CAPITAL LETTER O WITH ACUTE")
|
||||||
|
_(0xEF, 0x00D4, "# LATIN CAPITAL LETTER O WITH CIRCUMFLEX")
|
||||||
|
_(0xF0, 0xF8FF, "# Apple logo")
|
||||||
|
_(0xF1, 0x00D2, "# LATIN CAPITAL LETTER O WITH GRAVE")
|
||||||
|
_(0xF2, 0x00DA, "# LATIN CAPITAL LETTER U WITH ACUTE")
|
||||||
|
_(0xF3, 0x00DB, "# LATIN CAPITAL LETTER U WITH CIRCUMFLEX")
|
||||||
|
_(0xF4, 0x00D9, "# LATIN CAPITAL LETTER U WITH GRAVE")
|
||||||
|
_(0xF5, 0x0131, "# LATIN SMALL LETTER DOTLESS I")
|
||||||
|
_(0xF6, 0x02C6, "# MODIFIER LETTER CIRCUMFLEX ACCENT")
|
||||||
|
_(0xF7, 0x02DC, "# SMALL TILDE")
|
||||||
|
_(0xF8, 0x00AF, "# MACRON")
|
||||||
|
_(0xF9, 0x02D8, "# BREVE")
|
||||||
|
_(0xFA, 0x02D9, "# DOT ABOVE")
|
||||||
|
_(0xFB, 0x02DA, "# RING ABOVE")
|
||||||
|
_(0xFC, 0x00B8, "# CEDILLA")
|
||||||
|
_(0xFD, 0x02DD, "# DOUBLE ACUTE ACCENT")
|
||||||
|
_(0xFE, 0x02DB, "# OGONEK")
|
||||||
|
_(0xFF, 0x02C7, "# CARON")
|
32
make-version.rb
Normal file
32
make-version.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env ruby -w
|
||||||
|
|
||||||
|
# make-version version
|
||||||
|
|
||||||
|
def q(x)
|
||||||
|
# quote a string
|
||||||
|
'"' + x.gsub(/[\"]/, '\\\1' ) + '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
unless ARGV.length == 1
|
||||||
|
puts("Usage: make-version version")
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
VERSION = ARGV[0]
|
||||||
|
|
||||||
|
File.open("version.h", "w") {|f|
|
||||||
|
|
||||||
|
f.puts("#ifndef __version_h__")
|
||||||
|
f.puts("#define __version_h__")
|
||||||
|
f.puts("#define VERSION #{q(VERSION)}")
|
||||||
|
f.puts("#define VERSION_DATE #{q(Time.new.ctime)}")
|
||||||
|
f.puts("#endif")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
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
|
|
@ -1,123 +0,0 @@
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "mpw-shell.h"
|
|
||||||
|
|
||||||
%%{
|
|
||||||
machine classify;
|
|
||||||
alphtype unsigned char;
|
|
||||||
|
|
||||||
ws = [ \t];
|
|
||||||
|
|
||||||
IF = /if/i;
|
|
||||||
ELSE = /else/i;
|
|
||||||
END = /end/i;
|
|
||||||
EVALUATE = /evaluate/i;
|
|
||||||
|
|
||||||
|
|
||||||
main := |*
|
|
||||||
IF %eof{ return command_if; };
|
|
||||||
IF ws => {return command_if; };
|
|
||||||
|
|
||||||
ELSE %eof{ return command_else;};
|
|
||||||
ELSE ws => { return command_else; };
|
|
||||||
|
|
||||||
ELSE ws+ IF %eof{ return command_else_if; };
|
|
||||||
ELSE ws+ IF ws => {return command_else_if; };
|
|
||||||
|
|
||||||
END %eof{ return command_end; };
|
|
||||||
END ws => {return command_end; };
|
|
||||||
|
|
||||||
EVALUATE %eof{ return command_evaluate; };
|
|
||||||
EVALUATE ws => {return command_evaluate; };
|
|
||||||
|
|
||||||
|
|
||||||
*|;
|
|
||||||
|
|
||||||
}%%
|
|
||||||
|
|
||||||
|
|
||||||
int classify(const std::string &line) {
|
|
||||||
|
|
||||||
%% write data;
|
|
||||||
|
|
||||||
int cs;
|
|
||||||
int act;
|
|
||||||
|
|
||||||
const unsigned char *p = (const unsigned char *)line.data();
|
|
||||||
const unsigned char *pe = (const unsigned char *)line.data() + line.size();
|
|
||||||
const unsigned char *eof = pe;
|
|
||||||
const unsigned char *te, *ts;
|
|
||||||
|
|
||||||
%%write init;
|
|
||||||
|
|
||||||
%%write exec;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates a linked-list of commands. Why? Because it also checks
|
|
||||||
* for shell-special syntax (currently if / else /end only) and
|
|
||||||
* adds pointers to make executing them easier.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
command_ptr build_command(const std::vector<std::string> &lines) {
|
|
||||||
|
|
||||||
std::vector<command_ptr> if_stack;
|
|
||||||
|
|
||||||
command_ptr head;
|
|
||||||
command_ptr prev;
|
|
||||||
|
|
||||||
for (const auto &line : lines) {
|
|
||||||
if (line.empty()) continue;
|
|
||||||
|
|
||||||
int type = classify(line);
|
|
||||||
command_ptr c = std::make_shared<command>(type, line);
|
|
||||||
|
|
||||||
if (!head) head = c;
|
|
||||||
if (!prev) prev = c;
|
|
||||||
else {
|
|
||||||
prev->next = c;
|
|
||||||
prev = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if stack...
|
|
||||||
switch (type) {
|
|
||||||
case command_if:
|
|
||||||
if_stack.push_back(c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_else:
|
|
||||||
case command_else_if:
|
|
||||||
|
|
||||||
if (if_stack.empty()) {
|
|
||||||
throw std::runtime_error("### MPW Shell - Else must be within if ... end.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if_stack.back()->alternate = c;
|
|
||||||
if_stack.back() = c;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_end:
|
|
||||||
if (if_stack.empty()) {
|
|
||||||
throw std::runtime_error("### MPW Shell - Extra end command.");
|
|
||||||
}
|
|
||||||
if_stack.back()->alternate = c;
|
|
||||||
if_stack.pop_back();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!if_stack.empty()) {
|
|
||||||
throw std::runtime_error("### MPW Shell - Unterminated if command.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
|
@ -1,301 +0,0 @@
|
||||||
|
|
||||||
#line 1 "mpw-shell-commands.rl"
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
typedef std::shared_ptr<command> command_ptr;
|
|
||||||
typedef std::weak_ptr<command> weak_command_ptr;
|
|
||||||
|
|
||||||
class command {
|
|
||||||
enum type {
|
|
||||||
command_if = 1,
|
|
||||||
command_else,
|
|
||||||
command_else_if,
|
|
||||||
command_end
|
|
||||||
} = 0;
|
|
||||||
std::string line;
|
|
||||||
command_ptr next;
|
|
||||||
weak_command_ptr alternate; // if -> else -> end.
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#line 49 "mpw-shell-commands.rl"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int classify(const std::string &line) {
|
|
||||||
|
|
||||||
|
|
||||||
#line 35 "mpw-shell-commands.c"
|
|
||||||
static const int classify_start = 8;
|
|
||||||
static const int classify_first_final = 8;
|
|
||||||
static const int classify_error = 0;
|
|
||||||
|
|
||||||
static const int classify_en_main = 8;
|
|
||||||
|
|
||||||
|
|
||||||
#line 55 "mpw-shell-commands.rl"
|
|
||||||
|
|
||||||
int cs;
|
|
||||||
const unsigned char *p = (const unsigned char *)line.data();
|
|
||||||
const unsigned char *pe = (const unsigned char *)line.data() + line.size();
|
|
||||||
const unsigned char *eof = pe;
|
|
||||||
const unsigned char *te, *ts;
|
|
||||||
|
|
||||||
|
|
||||||
#line 52 "mpw-shell-commands.c"
|
|
||||||
{
|
|
||||||
cs = classify_start;
|
|
||||||
ts = 0;
|
|
||||||
te = 0;
|
|
||||||
act = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#line 63 "mpw-shell-commands.rl"
|
|
||||||
|
|
||||||
|
|
||||||
#line 63 "mpw-shell-commands.c"
|
|
||||||
{
|
|
||||||
if ( p == pe )
|
|
||||||
goto _test_eof;
|
|
||||||
switch ( cs )
|
|
||||||
{
|
|
||||||
tr5:
|
|
||||||
#line 40 "mpw-shell-commands.rl"
|
|
||||||
{{p = ((te))-1;}{ return command_else; }}
|
|
||||||
goto st8;
|
|
||||||
tr13:
|
|
||||||
#line 39 "mpw-shell-commands.rl"
|
|
||||||
{ return command_else;}
|
|
||||||
#line 39 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr14:
|
|
||||||
#line 39 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr16:
|
|
||||||
#line 40 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;{ return command_else; }}
|
|
||||||
goto st8;
|
|
||||||
tr17:
|
|
||||||
#line 42 "mpw-shell-commands.rl"
|
|
||||||
{ return command_else_if; }
|
|
||||||
#line 42 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr18:
|
|
||||||
#line 42 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr19:
|
|
||||||
#line 43 "mpw-shell-commands.rl"
|
|
||||||
{te = p+1;{return command_else_if; }}
|
|
||||||
goto st8;
|
|
||||||
tr20:
|
|
||||||
#line 45 "mpw-shell-commands.rl"
|
|
||||||
{ return command_end; }
|
|
||||||
#line 45 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr21:
|
|
||||||
#line 45 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr22:
|
|
||||||
#line 46 "mpw-shell-commands.rl"
|
|
||||||
{te = p+1;{return command_end; }}
|
|
||||||
goto st8;
|
|
||||||
tr23:
|
|
||||||
#line 36 "mpw-shell-commands.rl"
|
|
||||||
{ return command_if; }
|
|
||||||
#line 36 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr24:
|
|
||||||
#line 36 "mpw-shell-commands.rl"
|
|
||||||
{te = p;p--;}
|
|
||||||
goto st8;
|
|
||||||
tr25:
|
|
||||||
#line 37 "mpw-shell-commands.rl"
|
|
||||||
{te = p+1;{return command_if; }}
|
|
||||||
goto st8;
|
|
||||||
st8:
|
|
||||||
#line 1 "NONE"
|
|
||||||
{ts = 0;}
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof8;
|
|
||||||
case 8:
|
|
||||||
#line 1 "NONE"
|
|
||||||
{ts = p;}
|
|
||||||
#line 137 "mpw-shell-commands.c"
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 69u: goto st1;
|
|
||||||
case 73u: goto st7;
|
|
||||||
case 101u: goto st1;
|
|
||||||
case 105u: goto st7;
|
|
||||||
}
|
|
||||||
goto st0;
|
|
||||||
st0:
|
|
||||||
cs = 0;
|
|
||||||
goto _out;
|
|
||||||
st1:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof1;
|
|
||||||
case 1:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 76u: goto st2;
|
|
||||||
case 78u: goto st6;
|
|
||||||
case 108u: goto st2;
|
|
||||||
case 110u: goto st6;
|
|
||||||
}
|
|
||||||
goto st0;
|
|
||||||
st2:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof2;
|
|
||||||
case 2:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 83u: goto st3;
|
|
||||||
case 115u: goto st3;
|
|
||||||
}
|
|
||||||
goto st0;
|
|
||||||
st3:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof3;
|
|
||||||
case 3:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 69u: goto st9;
|
|
||||||
case 101u: goto st9;
|
|
||||||
}
|
|
||||||
goto st0;
|
|
||||||
st9:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof9;
|
|
||||||
case 9:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 9u: goto tr15;
|
|
||||||
case 32u: goto tr15;
|
|
||||||
}
|
|
||||||
goto tr14;
|
|
||||||
tr15:
|
|
||||||
#line 1 "NONE"
|
|
||||||
{te = p+1;}
|
|
||||||
goto st10;
|
|
||||||
st10:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof10;
|
|
||||||
case 10:
|
|
||||||
#line 194 "mpw-shell-commands.c"
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 9u: goto st4;
|
|
||||||
case 32u: goto st4;
|
|
||||||
case 73u: goto st5;
|
|
||||||
case 105u: goto st5;
|
|
||||||
}
|
|
||||||
goto tr16;
|
|
||||||
st4:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof4;
|
|
||||||
case 4:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 9u: goto st4;
|
|
||||||
case 32u: goto st4;
|
|
||||||
case 73u: goto st5;
|
|
||||||
case 105u: goto st5;
|
|
||||||
}
|
|
||||||
goto tr5;
|
|
||||||
st5:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof5;
|
|
||||||
case 5:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 70u: goto st11;
|
|
||||||
case 102u: goto st11;
|
|
||||||
}
|
|
||||||
goto tr5;
|
|
||||||
st11:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof11;
|
|
||||||
case 11:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 9u: goto tr19;
|
|
||||||
case 32u: goto tr19;
|
|
||||||
}
|
|
||||||
goto tr18;
|
|
||||||
st6:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof6;
|
|
||||||
case 6:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 68u: goto st12;
|
|
||||||
case 100u: goto st12;
|
|
||||||
}
|
|
||||||
goto st0;
|
|
||||||
st12:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof12;
|
|
||||||
case 12:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 9u: goto tr22;
|
|
||||||
case 32u: goto tr22;
|
|
||||||
}
|
|
||||||
goto tr21;
|
|
||||||
st7:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof7;
|
|
||||||
case 7:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 70u: goto st13;
|
|
||||||
case 102u: goto st13;
|
|
||||||
}
|
|
||||||
goto st0;
|
|
||||||
st13:
|
|
||||||
if ( ++p == pe )
|
|
||||||
goto _test_eof13;
|
|
||||||
case 13:
|
|
||||||
switch( (*p) ) {
|
|
||||||
case 9u: goto tr25;
|
|
||||||
case 32u: goto tr25;
|
|
||||||
}
|
|
||||||
goto tr24;
|
|
||||||
}
|
|
||||||
_test_eof8: cs = 8; goto _test_eof;
|
|
||||||
_test_eof1: cs = 1; goto _test_eof;
|
|
||||||
_test_eof2: cs = 2; goto _test_eof;
|
|
||||||
_test_eof3: cs = 3; goto _test_eof;
|
|
||||||
_test_eof9: cs = 9; goto _test_eof;
|
|
||||||
_test_eof10: cs = 10; goto _test_eof;
|
|
||||||
_test_eof4: cs = 4; goto _test_eof;
|
|
||||||
_test_eof5: cs = 5; goto _test_eof;
|
|
||||||
_test_eof11: cs = 11; goto _test_eof;
|
|
||||||
_test_eof6: cs = 6; goto _test_eof;
|
|
||||||
_test_eof12: cs = 12; goto _test_eof;
|
|
||||||
_test_eof7: cs = 7; goto _test_eof;
|
|
||||||
_test_eof13: cs = 13; goto _test_eof;
|
|
||||||
|
|
||||||
_test_eof: {}
|
|
||||||
if ( p == eof )
|
|
||||||
{
|
|
||||||
switch ( cs ) {
|
|
||||||
case 9: goto tr13;
|
|
||||||
case 10: goto tr16;
|
|
||||||
case 4: goto tr5;
|
|
||||||
case 5: goto tr5;
|
|
||||||
case 11: goto tr17;
|
|
||||||
case 12: goto tr20;
|
|
||||||
case 13: goto tr23;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_out: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#line 65 "mpw-shell-commands.rl"
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,297 +0,0 @@
|
||||||
#include "mpw-shell.h"
|
|
||||||
#include "fdset.h"
|
|
||||||
#include "value.h"
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sysexits.h>
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Relevant shell variables (not currently supported)
|
|
||||||
*
|
|
||||||
* Echo {Echo} # control the echoing of commands to diagnostic output
|
|
||||||
* Echo {Exit} # control script termination based on {Status}
|
|
||||||
* Echo {Test} # don't actually run anything.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef std::vector<std::string> vs;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
std::string &lowercase(std::string &s) {
|
|
||||||
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, int (*)(const std::vector<std::string> &, const fdmask &)> builtins = {
|
|
||||||
{"directory", builtin_directory},
|
|
||||||
{"echo", builtin_echo},
|
|
||||||
{"parameters", builtin_parameters},
|
|
||||||
{"quote", builtin_quote},
|
|
||||||
{"set", builtin_set},
|
|
||||||
{"unset", builtin_unset},
|
|
||||||
{"export", builtin_export},
|
|
||||||
{"unexport", builtin_unexport},
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef std::pair<int, command_ptr> icp;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
icp execute_all(command_ptr cmd);
|
|
||||||
|
|
||||||
// returns status and pointer to the next command to execute.
|
|
||||||
icp execute_if(command_ptr cmd) {
|
|
||||||
|
|
||||||
assert(cmd && cmd->type == command_if);
|
|
||||||
// evaluate condition...
|
|
||||||
// skip to else or end.
|
|
||||||
|
|
||||||
command_ptr head(cmd);
|
|
||||||
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
// find the end pointer.
|
|
||||||
// if ... end > file.text
|
|
||||||
// redirects all output within the block.
|
|
||||||
|
|
||||||
command_ptr end = head;
|
|
||||||
while (end && end->type != command_end) {
|
|
||||||
end = end->alternate.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fdmask fds; // todo -- inherit from block, can be parsed from end line.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fprintf(stdout, " %s ... %s\n", cmd->string.c_str(), end ? end->string.c_str() : "");
|
|
||||||
|
|
||||||
// todo -- indent levels.
|
|
||||||
while(cmd && cmd->type != command_end) {
|
|
||||||
|
|
||||||
int32_t e;
|
|
||||||
|
|
||||||
std::string s = cmd->string;
|
|
||||||
s = expand_vars(s, Environment);
|
|
||||||
|
|
||||||
auto tokens = tokenize(s, true);
|
|
||||||
|
|
||||||
std::reverse(tokens.begin(), tokens.end());
|
|
||||||
e = 0;
|
|
||||||
status = 0;
|
|
||||||
switch(cmd->type) {
|
|
||||||
case command_else_if:
|
|
||||||
tokens.pop_back();
|
|
||||||
case command_if:
|
|
||||||
tokens.pop_back();
|
|
||||||
try {
|
|
||||||
e = evaluate_expression("If", std::move(tokens));
|
|
||||||
} catch (std::exception &ex) {
|
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
|
||||||
status = -5;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case command_else:
|
|
||||||
e = 1;
|
|
||||||
if (tokens.size() > 1) {
|
|
||||||
fprintf(stderr, "### Else - Missing if keyword.\n");
|
|
||||||
fprintf(stderr, "# Usage - Else [if expression...]\n");
|
|
||||||
e = 0;
|
|
||||||
status = -3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (e) {
|
|
||||||
command_ptr tmp;
|
|
||||||
std::tie(status, tmp) = execute_all(cmd->next);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// skip to next condition.
|
|
||||||
cmd = cmd->alternate.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo -- print but don't execute remaining alternates
|
|
||||||
|
|
||||||
// print the end tokens... [ doesn't include other tokens.]
|
|
||||||
fprintf(stdout, " End\n");
|
|
||||||
|
|
||||||
return std::make_pair(status, end); // return end token -- will advance later.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int execute_evaluate(command_ptr cmd) {
|
|
||||||
|
|
||||||
fdmask fds; // todo -- inherit from block.
|
|
||||||
|
|
||||||
std::string s = cmd->string;
|
|
||||||
s = expand_vars(s, Environment);
|
|
||||||
|
|
||||||
|
|
||||||
fprintf(stdout, " %s\n", s.c_str());
|
|
||||||
|
|
||||||
auto tokens = tokenize(s, true);
|
|
||||||
|
|
||||||
return builtin_evaluate(std::move(tokens), fds);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int execute_external(const std::vector<std::string> &argv, const fdmask &fds) {
|
|
||||||
|
|
||||||
std::vector<char *> cargv;
|
|
||||||
cargv.reserve(argv.size() + 3);
|
|
||||||
|
|
||||||
int status;
|
|
||||||
int pid;
|
|
||||||
|
|
||||||
cargv.push_back((char *)"mpw");
|
|
||||||
//cargv.push_back((char *)"--shell");
|
|
||||||
|
|
||||||
unsigned offset = cargv.size();
|
|
||||||
|
|
||||||
|
|
||||||
std::transform(argv.begin(), argv.end(), std::back_inserter(cargv),
|
|
||||||
[](const std::string &s) { return strdup(s.c_str()); }
|
|
||||||
);
|
|
||||||
|
|
||||||
cargv.push_back(nullptr);
|
|
||||||
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
if (pid < 0) {
|
|
||||||
perror("fork: ");
|
|
||||||
exit(EX_OSERR);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (pid == 0) {
|
|
||||||
|
|
||||||
// also export environment...
|
|
||||||
|
|
||||||
// handle any indirection...
|
|
||||||
fds.dup();
|
|
||||||
|
|
||||||
execvp(cargv.front(), cargv.data());
|
|
||||||
perror("execvp: ");
|
|
||||||
exit(EX_OSERR);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::for_each(cargv.begin()+offset, cargv.end(), free);
|
|
||||||
|
|
||||||
for(;;) {
|
|
||||||
int status;
|
|
||||||
pid_t ok;
|
|
||||||
ok = waitpid(pid, &status, 0);
|
|
||||||
if (ok < 0) {
|
|
||||||
if (errno == EINTR) continue;
|
|
||||||
perror("waitpid:");
|
|
||||||
exit(EX_OSERR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WIFEXITED(status)) return WEXITSTATUS(status);
|
|
||||||
if (WIFSIGNALED(status)) return -1;
|
|
||||||
fprintf(stderr, "waitpid - unexpected result\n");
|
|
||||||
exit(EX_OSERR);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int execute_one(command_ptr cmd) {
|
|
||||||
|
|
||||||
if (!cmd) return 0;
|
|
||||||
|
|
||||||
assert(cmd && cmd->type == 0);
|
|
||||||
|
|
||||||
|
|
||||||
// todo -- before variable expansion,
|
|
||||||
// expand |, ||, && control structures.
|
|
||||||
// (possibly when classifing.)
|
|
||||||
|
|
||||||
std::string s = cmd->string;
|
|
||||||
s = expand_vars(s, Environment);
|
|
||||||
|
|
||||||
|
|
||||||
fprintf(stdout, " %s\n", s.c_str());
|
|
||||||
|
|
||||||
auto tokens = tokenize(s);
|
|
||||||
|
|
||||||
process p;
|
|
||||||
parse_tokens(std::move(tokens), p);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fdmask fds = p.fds.to_mask();
|
|
||||||
|
|
||||||
std::string name = p.arguments.front();
|
|
||||||
lowercase(name);
|
|
||||||
|
|
||||||
auto iter = builtins.find(name);
|
|
||||||
if (iter != builtins.end()) {
|
|
||||||
int status = iter->second(p.arguments, fds);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return execute_external(p.arguments, fds);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
icp execute_all(command_ptr cmd) {
|
|
||||||
if (!cmd) return std::make_pair(0, cmd);
|
|
||||||
|
|
||||||
int status;
|
|
||||||
|
|
||||||
while(cmd) {
|
|
||||||
|
|
||||||
unsigned type = cmd->type;
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case command_evaluate:
|
|
||||||
status = execute_evaluate(cmd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
status = execute_one(cmd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_if:
|
|
||||||
std::tie(status, cmd) = execute_if(cmd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_end:
|
|
||||||
case command_else:
|
|
||||||
case command_else_if:
|
|
||||||
return std::make_pair(status, cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
Environment["status"] = std::to_string(status);
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
// only if Environment["Exit"] ?
|
|
||||||
throw std::runtime_error("### MPW Shell - Execution of input terminated.");
|
|
||||||
}
|
|
||||||
cmd = cmd->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(status, cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int execute(command_ptr cmd) {
|
|
||||||
int status;
|
|
||||||
std::tie(status, cmd) = execute_all(cmd);
|
|
||||||
return status;
|
|
||||||
}
|
|
|
@ -3,132 +3,201 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <system_error>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine line_parser;
|
|
||||||
|
machine expand;
|
||||||
alphtype unsigned char;
|
alphtype unsigned char;
|
||||||
|
|
||||||
escape = 0xb6;
|
action push { scratch.push_back(fc); }
|
||||||
ws = [ \t];
|
action vinit { /* vinit */ ev.clear(); xcs = fcurs; fgoto vstring_state; }
|
||||||
nl = '\n';
|
action vpush { /* vpush */ ev.push_back(fc); }
|
||||||
|
action vfinish0 { /* vfinish0 */ fnext *xcs; }
|
||||||
action push_back {
|
action vfinish1 {
|
||||||
line.push_back(fc);
|
/* vfinish1 */
|
||||||
}
|
auto iter = env.find(ev);
|
||||||
action push_back_escape {
|
if (iter != env.end()) {
|
||||||
line.push_back(escape);
|
const std::string &s = iter->second;
|
||||||
line.push_back(fc);
|
scratch.append(s);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sstring =
|
|
||||||
['] $push_back
|
|
||||||
( (any-nl-[']) $push_back )*
|
|
||||||
['] $push_back
|
|
||||||
$err{
|
|
||||||
fprintf(stderr, "### MPW Shell - 's must occur in pairs.\n");
|
|
||||||
}
|
}
|
||||||
;
|
fgoto *xcs;
|
||||||
|
}
|
||||||
# same quoting logic as ' string
|
action vfinish2 {
|
||||||
vstring =
|
/* vfinish2 */
|
||||||
'{'
|
auto iter = env.find(ev);
|
||||||
( (any-nl-'}') ${var.push_back(fc); } )*
|
if (iter != env.end()) {
|
||||||
'}'
|
// quote special chars...
|
||||||
${
|
const std::string &s = iter->second;
|
||||||
if (!var.empty()) {
|
for (auto c : s) {
|
||||||
|
if (c == '\'' || c == '"' ) scratch.push_back(escape);
|
||||||
// flag to pass through vs "" ?
|
scratch.push_back(c);
|
||||||
auto iter = env.find(var);
|
|
||||||
if (iter == env.end()) {
|
|
||||||
if (env.passthrough()) {
|
|
||||||
line.push_back('{');
|
|
||||||
line.append(var);
|
|
||||||
line.push_back('}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
line.append((std::string)iter->second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var.clear();
|
|
||||||
}
|
}
|
||||||
$err{
|
fgoto *xcs;
|
||||||
fprintf(stderr, "### MPW Shell - {s must occur in pairs.\n");
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/* efinish2 */
|
||||||
|
|
||||||
|
std::string s = subshell(ev, env, fds);
|
||||||
|
for (auto c : s) {
|
||||||
|
if (c == '\'' || c == '"' ) scratch.push_back(escape);
|
||||||
|
scratch.push_back(c);
|
||||||
}
|
}
|
||||||
;
|
|
||||||
|
fgoto *xcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
action vstring_error{
|
||||||
|
throw vstring_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
action estring_error{
|
||||||
|
throw estring_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# double-quoted string.
|
escape = 0xb6;
|
||||||
# escape \n is ignored. others do nothing.
|
char = any - escape - ['"{`/\\];
|
||||||
dstring =
|
escape_seq = escape any;
|
||||||
["] $push_back
|
|
||||||
(
|
schar = [^'];
|
||||||
escape (
|
sstring = ['] schar** ['];
|
||||||
nl ${ /* esc newline */ }
|
|
||||||
|
|
fchar = [^/];
|
||||||
(any-nl) $push_back_escape
|
fstring = [/] fchar** [/];
|
||||||
)
|
|
||||||
|
|
bchar = [^\\];
|
||||||
vstring
|
bstring = [\\] bchar** [\\];
|
||||||
|
|
|
||||||
(any-escape-nl-["{]) $push_back
|
|
||||||
)* ["] $push_back
|
|
||||||
$err{
|
|
||||||
fprintf(stderr, "### MPW Shell - \"s must occur in pairs.\n");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
main :=
|
vchar = [^}] $vpush;
|
||||||
(
|
vchar1 = [^{}] $vpush;
|
||||||
sstring
|
|
||||||
|
|
vstring0 = '}' @vfinish0;
|
||||||
dstring
|
vstring1 = vchar1 vchar** '}' @vfinish1;
|
||||||
|
|
vstring2 = '{' vchar** '}}' @vfinish2;
|
||||||
vstring
|
|
||||||
|
|
vstring_state := (vstring0 | vstring1 | vstring2) $err(vstring_error);
|
||||||
escape any $push_back_escape
|
vstring = '{' $vinit;
|
||||||
|
|
|
||||||
(any-['"{]) $push_back
|
echar = (escape_seq | any - escape - [`]) $epush;
|
||||||
)*
|
|
||||||
;
|
estring1 = echar+ '`' @efinish1;
|
||||||
|
estring2 = '`' echar* '``' @efinish2;
|
||||||
|
|
||||||
|
estring_state := (estring1 | estring2) $err(estring_error);
|
||||||
|
estring = '`' $einit;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dchar = escape_seq | estring | vstring | (any - escape - [`{"]);
|
||||||
|
dstring = ["] dchar** ["] ;
|
||||||
|
|
||||||
|
|
||||||
|
main := (
|
||||||
|
escape_seq $push
|
||||||
|
| sstring $push
|
||||||
|
| fstring $push
|
||||||
|
| bstring $push
|
||||||
|
| dstring $push
|
||||||
|
| vstring
|
||||||
|
| estring
|
||||||
|
| char $push
|
||||||
|
)**;
|
||||||
|
|
||||||
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
%% write data;
|
||||||
|
|
||||||
|
|
||||||
%% write data;
|
std::string subshell(const std::string &s, Environment &env, const fdmask &fds) {
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* has to be done separately since you can do dumb stuff like:
|
|
||||||
* set q '"' ; echo {q} dsfsdf"
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::string expand_vars(const std::string &s, const Environment &env) {
|
|
||||||
|
|
||||||
if (s.find('{') == s.npos) return s;
|
|
||||||
std::string var;
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
int cs;
|
char temp[32] = "/tmp/mpw-shell-XXXXXXXX";
|
||||||
const unsigned char *p = (const unsigned char *)s.data();
|
|
||||||
const unsigned char *pe = (const unsigned char *)s.data() + s.size();
|
|
||||||
const unsigned char *eof = pe;
|
|
||||||
|
|
||||||
%%write init;
|
int fd = mkstemp(temp);
|
||||||
|
unlink(temp);
|
||||||
|
|
||||||
%%write exec;
|
fdset new_fds;
|
||||||
|
new_fds.set(1, fd);
|
||||||
|
|
||||||
return line;
|
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, Environment &env, const fdmask &fds) {
|
||||||
|
if (s.find_first_of("{`", 0, 2) == s.npos) return s;
|
||||||
|
|
||||||
|
int cs;
|
||||||
|
int xcs;
|
||||||
|
|
||||||
|
const unsigned char *p = (const unsigned char *)s.data();
|
||||||
|
const unsigned char *pe = p + s.size();
|
||||||
|
const unsigned char *eof = pe;
|
||||||
|
|
||||||
|
std::string scratch;
|
||||||
|
std::string ev;
|
||||||
|
|
||||||
|
scratch.reserve(s.size());
|
||||||
|
%% write init;
|
||||||
|
%% write exec;
|
||||||
|
|
||||||
|
return scratch;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "mpw-regex.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -33,12 +35,12 @@ T pop(std::vector<T> &v) {
|
||||||
|
|
||||||
void open_error(const std::string &name) {
|
void open_error(const std::string &name) {
|
||||||
|
|
||||||
std::string error = "### MPW Shell - Unable to open ";
|
std::string error = "MPW Shell - Unable to open ";
|
||||||
error.push_back('"');
|
error.push_back('"');
|
||||||
error.append(name);
|
error.append(name);
|
||||||
error.push_back('"');
|
error.push_back('"');
|
||||||
error.push_back('.');
|
error.push_back('.');
|
||||||
throw std::runtime_error(error);
|
throw mpw_error(-4, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
int open(const std::string &name, int flags) {
|
int open(const std::string &name, int flags) {
|
||||||
|
@ -104,7 +106,7 @@ void parse_tokens(std::vector<token> &&tokens, process &p) {
|
||||||
fd_bits = (1 << 1) + (1 << 2);
|
fd_bits = (1 << 1) + (1 << 2);
|
||||||
goto redir;
|
goto redir;
|
||||||
|
|
||||||
// ≥, ≥≥ -- redirect stdout.
|
// ≥, ≥≥ -- redirect stderr.
|
||||||
case 0xb3: // ≥
|
case 0xb3: // ≥
|
||||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||||
fd_bits = 1 << 2;
|
fd_bits = 1 << 2;
|
||||||
|
@ -119,7 +121,7 @@ void parse_tokens(std::vector<token> &&tokens, process &p) {
|
||||||
|
|
||||||
{
|
{
|
||||||
if (tokens.empty()) {
|
if (tokens.empty()) {
|
||||||
throw std::runtime_error("### MPW Shell - Missing file name.");
|
throw mpw_error(-4, "MPW Shell - Missing file name.");
|
||||||
}
|
}
|
||||||
token name = pop(tokens);
|
token name = pop(tokens);
|
||||||
int fd = open(name.string, flags);
|
int fd = open(name.string, flags);
|
||||||
|
@ -171,8 +173,8 @@ class expression_parser {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
expression_parser(const std::string &n, std::vector<token> &&t) :
|
expression_parser(Environment &e, const std::string &n, std::vector<token> &&t) :
|
||||||
name(n), tokens(std::move(t))
|
environment(e), name(n), tokens(std::move(t))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
expression_parser(const expression_parser &) = delete;
|
expression_parser(const expression_parser &) = delete;
|
||||||
|
@ -193,6 +195,7 @@ private:
|
||||||
|
|
||||||
|
|
||||||
value eval(int op, value &lhs, value &rhs);
|
value eval(int op, value &lhs, value &rhs);
|
||||||
|
value eval_regex(value &lhs, value &rhs);
|
||||||
|
|
||||||
[[noreturn]] void expect_binary_operator();
|
[[noreturn]] void expect_binary_operator();
|
||||||
[[noreturn]] void end_of_expression();
|
[[noreturn]] void end_of_expression();
|
||||||
|
@ -206,6 +209,7 @@ private:
|
||||||
if (!tokens.empty()) tokens.pop_back();
|
if (!tokens.empty()) tokens.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Environment &environment;
|
||||||
const std::string &name;
|
const std::string &name;
|
||||||
std::vector<token> tokens;
|
std::vector<token> tokens;
|
||||||
};
|
};
|
||||||
|
@ -224,23 +228,23 @@ void expression_parser::expect_binary_operator() {
|
||||||
token t = next();
|
token t = next();
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
error = "### " + name;
|
error = name;
|
||||||
error += " - Expected a binary operator when \"";
|
error += " - Expected a binary operator when \"";
|
||||||
error += t.string;
|
error += t.string;
|
||||||
error += "\" was encountered.";
|
error += "\" was encountered.";
|
||||||
throw std::runtime_error(error);
|
throw mpw_error(-5, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void expression_parser::end_of_expression() {
|
void expression_parser::end_of_expression() {
|
||||||
std::string error;
|
std::string error;
|
||||||
error = "### " + name + " - Unexpected end of expression.";
|
error = name + " - Unexpected end of expression.";
|
||||||
throw std::runtime_error(error);
|
throw mpw_error(-5, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void expression_parser::divide_by_zero() {
|
void expression_parser::divide_by_zero() {
|
||||||
std::string error;
|
std::string error;
|
||||||
error = "### " + name + " - Attempt to divide by zero.";
|
error = name + " - Attempt to divide by zero.";
|
||||||
throw std::runtime_error(error);
|
throw mpw_error(-5, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -318,12 +322,14 @@ int expression_parser::precedence(int op) {
|
||||||
case '<=':
|
case '<=':
|
||||||
case '>':
|
case '>':
|
||||||
case '>=':
|
case '>=':
|
||||||
|
case 0xb2:
|
||||||
|
case 0xb3:
|
||||||
return 6;
|
return 6;
|
||||||
|
|
||||||
case '==':
|
case '==':
|
||||||
case '!=':
|
case '!=':
|
||||||
case token::equivalent:
|
case '=~':
|
||||||
case token::not_equivalent:
|
case '!~':
|
||||||
return 7;
|
return 7;
|
||||||
case '&':
|
case '&':
|
||||||
return 8;
|
return 8;
|
||||||
|
@ -340,6 +346,25 @@ int expression_parser::precedence(int op) {
|
||||||
//throw std::runtime_error("unimplemented 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) {
|
value expression_parser::eval(int op, value &lhs, value &rhs) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
|
|
||||||
|
@ -365,16 +390,18 @@ value expression_parser::eval(int op, value &lhs, value &rhs) {
|
||||||
return lhs.to_number() < rhs.to_number();
|
return lhs.to_number() < rhs.to_number();
|
||||||
|
|
||||||
case '<=':
|
case '<=':
|
||||||
|
case 0xb2:
|
||||||
return lhs.to_number() <= rhs.to_number();
|
return lhs.to_number() <= rhs.to_number();
|
||||||
|
|
||||||
case '>=':
|
case '>=':
|
||||||
|
case 0xb3:
|
||||||
return lhs.to_number() >= rhs.to_number();
|
return lhs.to_number() >= rhs.to_number();
|
||||||
|
|
||||||
case '>>':
|
case '>>':
|
||||||
return lhs.to_number() >> rhs.to_number();
|
return lhs.to_number() >> rhs.to_number();
|
||||||
|
|
||||||
case '<<':
|
case '<<':
|
||||||
return lhs.to_number() >> rhs.to_number();
|
return lhs.to_number() << rhs.to_number();
|
||||||
|
|
||||||
// logical || . NaN ok
|
// logical || . NaN ok
|
||||||
case '||':
|
case '||':
|
||||||
|
@ -406,6 +433,12 @@ value expression_parser::eval(int op, value &lhs, value &rhs) {
|
||||||
return lhs.string != rhs.string;
|
return lhs.string != rhs.string;
|
||||||
|
|
||||||
|
|
||||||
|
case '=~':
|
||||||
|
return eval_regex(lhs, rhs);
|
||||||
|
|
||||||
|
case '!~':
|
||||||
|
return !eval_regex(lhs, rhs).number;
|
||||||
|
|
||||||
}
|
}
|
||||||
// todo...
|
// todo...
|
||||||
throw std::runtime_error("unimplemented op");
|
throw std::runtime_error("unimplemented op");
|
||||||
|
@ -462,14 +495,14 @@ int32_t expression_parser::evaluate() {
|
||||||
value v = binary();
|
value v = binary();
|
||||||
if (!tokens.empty()) {
|
if (!tokens.empty()) {
|
||||||
if (tokens.back().type == ')')
|
if (tokens.back().type == ')')
|
||||||
throw std::runtime_error("### MPW Shell - Extra ) command.");
|
throw mpw_error(-3, "MPW Shell - Extra ) command.");
|
||||||
throw std::runtime_error("evaluation stack error."); // ?? should be caught above.
|
throw std::runtime_error("evaluation stack error."); // ?? should be caught above.
|
||||||
}
|
}
|
||||||
return v.to_number(1);
|
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();
|
return p.evaluate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,31 +7,19 @@ bool must_quote(const std::string &s){
|
||||||
alphtype unsigned char;
|
alphtype unsigned char;
|
||||||
|
|
||||||
quotable = (
|
quotable = (
|
||||||
[ \t\r\n]
|
[ \t\r\n]
|
||||||
|
|
| 0x00
|
||||||
0x00
|
| [0x80-0xff]
|
||||||
|
|
| [+#;&|()'"/\\{}`?*<>]
|
||||||
[0x80-0xff]
|
| '-'
|
||||||
|
|
| '['
|
||||||
[+#;&|()'"/\\{}`?*<>]
|
| ']'
|
||||||
|
|
|
||||||
'-'
|
|
||||||
|
|
|
||||||
'['
|
|
||||||
|
|
|
||||||
']'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#simpler just to say what's ok.
|
#simpler just to say what's ok.
|
||||||
normal = [A-Za-z0-9_.:];
|
normal = [A-Za-z0-9_.:];
|
||||||
|
|
||||||
main :=
|
main := normal*;
|
||||||
(
|
|
||||||
normal
|
|
||||||
|
|
|
||||||
(any-normal) ${return true;}
|
|
||||||
)*
|
|
||||||
;
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
%%write data;
|
%%write data;
|
||||||
|
@ -43,7 +31,8 @@ bool must_quote(const std::string &s){
|
||||||
|
|
||||||
%%write init;
|
%%write init;
|
||||||
%%write exec;
|
%%write exec;
|
||||||
return false;
|
|
||||||
|
return cs == must_quote_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
|
@ -1,379 +0,0 @@
|
||||||
#include "mpw-shell.h"
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
%%{
|
|
||||||
machine classify;
|
|
||||||
alphtype unsigned char;
|
|
||||||
|
|
||||||
ws = [ \t];
|
|
||||||
|
|
||||||
IF = /if/i;
|
|
||||||
ELSE = /else/i;
|
|
||||||
END = /end/i;
|
|
||||||
BEGIN = /begin/i;
|
|
||||||
EVALUATE = /evaluate/i;
|
|
||||||
|
|
||||||
|
|
||||||
main := |*
|
|
||||||
IF %eof{ return command_if; };
|
|
||||||
IF ws => {return command_if; };
|
|
||||||
|
|
||||||
ELSE %eof{ return command_else;};
|
|
||||||
ELSE ws => { return command_else; };
|
|
||||||
|
|
||||||
ELSE ws+ IF %eof{ return command_else_if; };
|
|
||||||
ELSE ws+ IF ws => {return command_else_if; };
|
|
||||||
|
|
||||||
END %eof{ return command_end; };
|
|
||||||
END ws => {return command_end; };
|
|
||||||
|
|
||||||
BEGIN %eof{ return command_begin; };
|
|
||||||
BEGIN ws => {return command_begin; };
|
|
||||||
|
|
||||||
EVALUATE %eof{ return command_evaluate; };
|
|
||||||
EVALUATE ws => {return command_evaluate; };
|
|
||||||
|
|
||||||
*|;
|
|
||||||
|
|
||||||
}%%
|
|
||||||
|
|
||||||
|
|
||||||
static int classify(const std::string &line) {
|
|
||||||
|
|
||||||
%%machine classify;
|
|
||||||
%% write data;
|
|
||||||
|
|
||||||
int cs;
|
|
||||||
int act;
|
|
||||||
|
|
||||||
const unsigned char *p = (const unsigned char *)line.data();
|
|
||||||
const unsigned char *pe = (const unsigned char *)line.data() + line.size();
|
|
||||||
const unsigned char *eof = pe;
|
|
||||||
const unsigned char *te, *ts;
|
|
||||||
|
|
||||||
%%write init;
|
|
||||||
|
|
||||||
%%write exec;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this state machine splits input into lines.
|
|
||||||
* only new-line escapes are removed.
|
|
||||||
* "", '', and {} are also matched.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* from experimentation, mpw splits on ; after variable expansion;
|
|
||||||
* this splits before. something stupid like:
|
|
||||||
* set q '"'; echo {q} ; "
|
|
||||||
* will not be handled correctly. oh well.
|
|
||||||
* (should probably just drop that and we can then combine tokenizing w/
|
|
||||||
* variable expansion)
|
|
||||||
*/
|
|
||||||
%%{
|
|
||||||
machine line_parser;
|
|
||||||
alphtype unsigned char;
|
|
||||||
|
|
||||||
|
|
||||||
escape = 0xb6;
|
|
||||||
ws = [ \t];
|
|
||||||
nl = ('\n' | '\r');
|
|
||||||
|
|
||||||
action add_line {
|
|
||||||
/* strip trailing ws */
|
|
||||||
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
|
||||||
if (!scratch.empty()) {
|
|
||||||
command_ptr cmd = std::make_shared<command>(std::move(scratch));
|
|
||||||
cmd->line = start_line;
|
|
||||||
start_line = line;
|
|
||||||
program.emplace_back(std::move(cmd));
|
|
||||||
}
|
|
||||||
scratch.clear();
|
|
||||||
fgoto main;
|
|
||||||
}
|
|
||||||
|
|
||||||
action push_back {
|
|
||||||
scratch.push_back(fc);
|
|
||||||
}
|
|
||||||
|
|
||||||
action push_back_escape {
|
|
||||||
scratch.push_back(escape);
|
|
||||||
scratch.push_back(fc);
|
|
||||||
}
|
|
||||||
|
|
||||||
comment = '#' (any-nl)*;
|
|
||||||
|
|
||||||
escape_seq =
|
|
||||||
escape
|
|
||||||
(
|
|
||||||
nl ${ /* esc newline */ line++; }
|
|
||||||
|
|
|
||||||
(any-nl) $push_back_escape
|
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
# single-quoted string. only escape \n is special.
|
|
||||||
# handling is so stupid I'm not going to support it.
|
|
||||||
|
|
||||||
sstring =
|
|
||||||
['] $push_back
|
|
||||||
( (any-nl-[']) $push_back )*
|
|
||||||
['] $push_back
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - 's must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
# same quoting logic as ' string
|
|
||||||
vstring =
|
|
||||||
'{' $push_back
|
|
||||||
( (any-nl-'}') $push_back )*
|
|
||||||
'}' $push_back
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - {s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
# double-quoted string.
|
|
||||||
# escape \n is ignored. others do nothing.
|
|
||||||
dstring =
|
|
||||||
["] $push_back
|
|
||||||
(
|
|
||||||
escape_seq
|
|
||||||
|
|
|
||||||
vstring
|
|
||||||
|
|
|
||||||
(any-escape-nl-["{]) $push_back
|
|
||||||
)* ["] $push_back
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - \"s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
# this is a mess ...
|
|
||||||
coalesce_ws =
|
|
||||||
ws
|
|
||||||
(
|
|
||||||
ws
|
|
||||||
|
|
|
||||||
escape nl ${ line++; }
|
|
||||||
)*
|
|
||||||
<:
|
|
||||||
any ${ scratch.push_back(' '); fhold; }
|
|
||||||
;
|
|
||||||
|
|
||||||
line :=
|
|
||||||
(
|
|
||||||
sstring
|
|
||||||
|
|
|
||||||
dstring
|
|
||||||
|
|
|
||||||
vstring
|
|
||||||
|
|
|
||||||
[;] $add_line
|
|
||||||
|
|
|
||||||
escape_seq
|
|
||||||
|
|
|
||||||
coalesce_ws
|
|
||||||
|
|
|
||||||
(any-escape-nl-ws-[;#'"{]) $push_back
|
|
||||||
)*
|
|
||||||
comment?
|
|
||||||
nl ${ line++; } $add_line
|
|
||||||
;
|
|
||||||
|
|
||||||
main :=
|
|
||||||
# strip leading whitespace.
|
|
||||||
ws*
|
|
||||||
<: # left guard -- higher priority to ws.
|
|
||||||
any ${ fhold; fgoto line; }
|
|
||||||
;
|
|
||||||
|
|
||||||
}%%
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class line_parser {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void process(const void *data, size_t size) {
|
|
||||||
process((const unsigned char *)data, size, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
command_ptr finish() {
|
|
||||||
process((const unsigned char *)"\n\n", 2, true);
|
|
||||||
return build_program();
|
|
||||||
}
|
|
||||||
|
|
||||||
line_parser();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
%% machine line_parser;
|
|
||||||
%% write data;
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<command_ptr> program;
|
|
||||||
std::string scratch;
|
|
||||||
int line = 1;
|
|
||||||
int cs;
|
|
||||||
|
|
||||||
command_ptr build_program();
|
|
||||||
void process(const unsigned char *data, size_t size, bool final);
|
|
||||||
};
|
|
||||||
|
|
||||||
line_parser::line_parser() {
|
|
||||||
%% machine line_parser;
|
|
||||||
%% write init;
|
|
||||||
}
|
|
||||||
|
|
||||||
void line_parser::process(const unsigned char *data, size_t size, bool final) {
|
|
||||||
|
|
||||||
int start_line;
|
|
||||||
|
|
||||||
const unsigned char *p = data;
|
|
||||||
const unsigned char *pe = data + size;
|
|
||||||
const unsigned char *eof = nullptr;
|
|
||||||
|
|
||||||
if (final)
|
|
||||||
eof = pe;
|
|
||||||
|
|
||||||
start_line = line;
|
|
||||||
%% machine line_parser;
|
|
||||||
%% write exec;
|
|
||||||
|
|
||||||
if (cs == line_parser_error) {
|
|
||||||
throw std::runtime_error("MPW Shell - Lexer error.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cs != line_parser_start && final) {
|
|
||||||
// will this happen?
|
|
||||||
throw std::runtime_error("MPW Shell - Lexer error.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates a linked-list of commands. Why? Because it also checks
|
|
||||||
* for shell-special syntax (currently if / else /end only) and
|
|
||||||
* adds pointers to make executing them easier.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// todo -- use recursive descent parser, support begin/end, (), ||, &&, etc.
|
|
||||||
command_ptr line_parser::build_program() {
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<command_ptr> if_stack;
|
|
||||||
|
|
||||||
command_ptr head;
|
|
||||||
command_ptr ptr;
|
|
||||||
|
|
||||||
if (program.empty()) return head;
|
|
||||||
|
|
||||||
std::reverse(program.begin(), program.end());
|
|
||||||
|
|
||||||
head = program.back();
|
|
||||||
|
|
||||||
while (!program.empty()) {
|
|
||||||
|
|
||||||
if (ptr) ptr->next = program.back();
|
|
||||||
|
|
||||||
ptr = std::move(program.back());
|
|
||||||
program.pop_back();
|
|
||||||
|
|
||||||
int type = ptr->type = classify(ptr->string);
|
|
||||||
|
|
||||||
ptr->level = if_stack.size();
|
|
||||||
|
|
||||||
// if stack...
|
|
||||||
switch (type) {
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_if:
|
|
||||||
if_stack.push_back(ptr);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_else:
|
|
||||||
case command_else_if:
|
|
||||||
|
|
||||||
if (if_stack.empty()) {
|
|
||||||
throw std::runtime_error("### MPW Shell - Else must be within if ... end.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr->level--;
|
|
||||||
if_stack.back()->alternate = ptr;
|
|
||||||
if_stack.back() = ptr;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case command_end:
|
|
||||||
if (if_stack.empty()) {
|
|
||||||
throw std::runtime_error("### MPW Shell - Extra end command.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr->level--;
|
|
||||||
if_stack.back()->alternate = ptr;
|
|
||||||
if_stack.pop_back();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!if_stack.empty()) {
|
|
||||||
throw std::runtime_error("### MPW Shell - Unterminated if command.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
command_ptr read_fd(int fd) {
|
|
||||||
unsigned char buffer[1024];
|
|
||||||
|
|
||||||
line_parser p;
|
|
||||||
|
|
||||||
for(;;) {
|
|
||||||
ssize_t s = read(fd, buffer, sizeof(buffer));
|
|
||||||
if (s < 0) {
|
|
||||||
if (errno == EINTR) continue;
|
|
||||||
throw std::runtime_error("MPW Shell - Read error.");
|
|
||||||
}
|
|
||||||
if (s == 0) break;
|
|
||||||
p.process(buffer, s);
|
|
||||||
}
|
|
||||||
return p.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
command_ptr read_file(const std::string &name) {
|
|
||||||
int fd;
|
|
||||||
fd = open(name.c_str(), O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
throw std::runtime_error("MPW Shell - Unable to open file " + name + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tmp = read_fd(fd);
|
|
||||||
close(fd);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
command_ptr read_string(const std::string &s) {
|
|
||||||
line_parser p;
|
|
||||||
|
|
||||||
p.process(s.data(), s.size());
|
|
||||||
return p.finish();
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine tokenizer;
|
machine tokenizer;
|
||||||
|
@ -10,99 +11,48 @@
|
||||||
|
|
||||||
|
|
||||||
escape = 0xb6;
|
escape = 0xb6;
|
||||||
ws = [ \t];
|
ws = [ \t\n\r];
|
||||||
nl = '\n' | '\r';
|
|
||||||
|
|
||||||
action push_token {
|
action push_token {
|
||||||
if (!scratch.empty() || quoted) {
|
if (!scratch.empty()) {
|
||||||
tokens.emplace_back(std::move(scratch));
|
tokens.emplace_back(std::move(scratch));
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
quoted = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action push_back {
|
action push {
|
||||||
scratch.push_back(fc);
|
scratch.push_back(fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
# vstring_quoted =
|
action push_string {
|
||||||
# [{]
|
scratch.append(ts, te);
|
||||||
# ( (any-nl-[}]) ${ var.push_back(fc); } )*
|
}
|
||||||
# [}]
|
|
||||||
# %{
|
|
||||||
# auto iter = Environment.find(var);
|
|
||||||
# if (iter != Environment.end() {
|
|
||||||
# scratch.append(iter->second);
|
|
||||||
# })
|
|
||||||
# var.clear();
|
|
||||||
# }
|
|
||||||
# $err{
|
|
||||||
# throw std::runtime_error("### MPW Shell - '{ must occur in pairs.");
|
|
||||||
# }
|
|
||||||
# ;
|
|
||||||
|
|
||||||
# vstring_unqoted =
|
schar = [^'] ;
|
||||||
# [{]
|
sstring = ['] schar** ['] $err{ throw sstring_error(); } ;
|
||||||
# ( (any-nl-[}]) ${ var.push_back(fc); } )*
|
|
||||||
# [}]
|
|
||||||
# %{
|
|
||||||
# auto iter = Environment.find(var);
|
|
||||||
# if (iter != Environment.end() {
|
|
||||||
# // re-parse. ", ', { are not
|
|
||||||
# // special. all others are treated normally.
|
|
||||||
# })
|
|
||||||
# var.clear();
|
|
||||||
# }
|
|
||||||
# $err{
|
|
||||||
# throw std::runtime_error("### MPW Shell - '{ must occur in pairs.");
|
|
||||||
# }
|
|
||||||
# ;
|
|
||||||
|
|
||||||
sstring =
|
escape_seq = escape any ;
|
||||||
[']
|
|
||||||
( (any-nl-[']) $push_back )*
|
|
||||||
[']
|
|
||||||
${ quoted = true; }
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - 's must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
escape_seq =
|
|
||||||
escape
|
|
||||||
(
|
|
||||||
'f' ${scratch.push_back('\f'); }
|
|
||||||
|
|
|
||||||
'n' ${scratch.push_back('\n'); /* \r ? */ }
|
|
||||||
|
|
|
||||||
't' ${scratch.push_back('\t'); }
|
|
||||||
|
|
|
||||||
any-[fnt] $push_back
|
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
# double-quoted string.
|
# double-quoted string.
|
||||||
dstring =
|
dchar = escape_seq | (any - escape - ["]);
|
||||||
["]
|
dstring = ["] dchar** ["] $err{ throw dstring_error(); } ;
|
||||||
(
|
|
||||||
escape_seq
|
|
||||||
|
|
|
||||||
(any-escape-["]) $push_back
|
|
||||||
)*
|
|
||||||
["]
|
|
||||||
${ quoted = true; }
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - \"s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
# 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 }
|
action eval { eval }
|
||||||
|
|
||||||
# > == start state (single char tokens or common prefix)
|
# > == start state (single char tokens or common prefix)
|
||||||
# % == final state (multi char tokens w/ unique prefix)
|
# % == final state (multi char tokens w/ unique prefix)
|
||||||
# $ == all states
|
# $ == all states
|
||||||
|
char = any - ['"/\\];
|
||||||
main := |*
|
main := |*
|
||||||
ws+ >push_token;
|
ws+ >push_token;
|
||||||
'>>' %push_token => { tokens.emplace_back(">>", '>>'); };
|
'>>' %push_token => { tokens.emplace_back(">>", '>>'); };
|
||||||
|
@ -110,20 +60,48 @@
|
||||||
|
|
||||||
'<' %push_token => { tokens.emplace_back("<", '<'); };
|
'<' %push_token => { tokens.emplace_back("<", '<'); };
|
||||||
|
|
||||||
'||' %push_token => { tokens.emplace_back("||", '||'); };
|
# macroman ∑, ∑∑
|
||||||
'|' %push_token => { tokens.emplace_back("|", '|'); };
|
0xb7 0xb7 %push_token => { tokens.emplace_back("\xb7\xb7", 0xb7b7); };
|
||||||
|
0xb7 %push_token => { tokens.emplace_back("\xb7", 0xb7); };
|
||||||
|
|
||||||
'&&'
|
# macroman ≥, ≥≥
|
||||||
%push_token => { tokens.emplace_back("&&", '&&'); };
|
0xb3 0xb3 %push_token => { tokens.emplace_back("\xb3\xb3", 0xb3b3); };
|
||||||
|
0xb3 %push_token => { tokens.emplace_back("\xb3", 0xb3); };
|
||||||
|
|
||||||
# eval-only.
|
# 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
|
||||||
|
%push_token => { tokens.emplace_back("|", '|'); };
|
||||||
|
|
||||||
|
'&&' when eval
|
||||||
|
%push_token => { tokens.emplace_back("&&", '&&'); };
|
||||||
|
|
||||||
'(' when eval
|
'(' when eval
|
||||||
%push_token => { tokens.emplace_back("(", '('); };
|
%push_token => { tokens.emplace_back("(", '('); };
|
||||||
|
|
||||||
')' when eval
|
')' when eval
|
||||||
%push_token => { tokens.emplace_back(")", ')'); };
|
%push_token => { tokens.emplace_back(")", ')'); };
|
||||||
|
|
||||||
|
'<>' when eval
|
||||||
|
%push_token => { tokens.emplace_back("<>", '!='); };
|
||||||
|
|
||||||
'<<' when eval
|
'<<' when eval
|
||||||
%push_token => { tokens.emplace_back("<<", '<<'); };
|
%push_token => { tokens.emplace_back("<<", '<<'); };
|
||||||
|
@ -154,7 +132,7 @@
|
||||||
|
|
||||||
|
|
||||||
'-' when eval
|
'-' when eval
|
||||||
%push_token => { tokens.emplace_back("+", '-'); };
|
%push_token => { tokens.emplace_back("-", '-'); };
|
||||||
|
|
||||||
'!' when eval
|
'!' when eval
|
||||||
%push_token => { tokens.emplace_back("!", '!'); };
|
%push_token => { tokens.emplace_back("!", '!'); };
|
||||||
|
@ -175,35 +153,37 @@
|
||||||
'-=' when eval
|
'-=' when eval
|
||||||
%push_token => { tokens.emplace_back("-=", '-='); };
|
%push_token => { tokens.emplace_back("-=", '-='); };
|
||||||
|
|
||||||
|
'=~' when eval
|
||||||
|
%push_token => { tokens.emplace_back("=~", '=~'); };
|
||||||
|
|
||||||
sstring ;
|
'!~' when eval
|
||||||
dstring ;
|
%push_token => { tokens.emplace_back("!~", '!~'); };
|
||||||
escape_seq;
|
|
||||||
|
|
||||||
(any-escape-['"]) => push_back; # { scratch.append(ts, te); };
|
sstring => push_string;
|
||||||
#(any-escape-ws-[>'"])+ => { scratch.append(ts, te); };
|
dstring => push_string;
|
||||||
|
fstring => push_string;
|
||||||
|
bstring => push_string;
|
||||||
|
escape_seq => push_string;
|
||||||
|
|
||||||
|
char => push;
|
||||||
*|
|
*|
|
||||||
;
|
;
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inline void replace_eval_token(token &t) {
|
void replace_eval_token(token &t) {
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
|
|
||||||
machine eval_keywords;
|
machine eval_keywords;
|
||||||
|
|
||||||
main :=
|
main :=
|
||||||
/and/i %{ t.type = '&&'; }
|
'and'i %{ t.type = '&&'; }
|
||||||
|
|
| 'or'i %{ t.type = '||'; }
|
||||||
/or/i %{ t.type = '||'; }
|
| 'not'i %{ t.type = '!'; }
|
||||||
|
|
| 'div'i %{ t.type = '/'; }
|
||||||
/not/i %{ t.type = '!'; }
|
| 'mod'i %{ t.type = '%'; }
|
||||||
|
|
|
||||||
/div/i %{ t.type = '/'; }
|
|
||||||
|
|
|
||||||
/mod/i %{ t.type = '%'; }
|
|
||||||
;
|
;
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
@ -216,15 +196,78 @@ inline void replace_eval_token(token &t) {
|
||||||
const char *pe = t.string.data() + t.string.size();
|
const char *pe = t.string.data() + t.string.size();
|
||||||
const char *eof = pe;
|
const char *eof = pe;
|
||||||
int cs;
|
int cs;
|
||||||
|
|
||||||
%%write init;
|
%%write init;
|
||||||
|
|
||||||
%%write exec;
|
%%write exec;
|
||||||
}
|
}
|
||||||
std::vector<token> tokenize(const std::string &s, bool eval)
|
|
||||||
|
|
||||||
|
void unquote(token &t) {
|
||||||
|
|
||||||
|
if (t.string.find_first_of("'\"\xb6", 0, 3) == t.string.npos) return;
|
||||||
|
|
||||||
|
int cs;
|
||||||
|
const unsigned char *p = (const unsigned char *)t.string.data();
|
||||||
|
const unsigned char *pe = p + t.string.length();
|
||||||
|
const unsigned char *eof = pe;
|
||||||
|
|
||||||
|
std::string scratch;
|
||||||
|
scratch.reserve(t.string.length());
|
||||||
|
%%{
|
||||||
|
|
||||||
|
machine unquote;
|
||||||
|
alphtype unsigned char;
|
||||||
|
|
||||||
|
action push { scratch.push_back(fc); }
|
||||||
|
escape = 0xb6;
|
||||||
|
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'); }
|
||||||
|
| 't' ${ scratch.push_back('\t'); }
|
||||||
|
| [^fnt] ${ scratch.push_back(fc); }
|
||||||
|
;
|
||||||
|
|
||||||
|
escape_seq = escape $err{ scratch.push_back(escape); } ecode;
|
||||||
|
|
||||||
|
dchar = escape ecode | (any - escape - ["]) $push;
|
||||||
|
dstring = ["] dchar** ["];
|
||||||
|
|
||||||
|
main := (
|
||||||
|
escape_seq
|
||||||
|
| sstring
|
||||||
|
| fstring
|
||||||
|
| bstring
|
||||||
|
| dstring
|
||||||
|
| char $push
|
||||||
|
)**;
|
||||||
|
|
||||||
|
write data;
|
||||||
|
write init;
|
||||||
|
write exec;
|
||||||
|
}%%
|
||||||
|
|
||||||
|
t.string = std::move(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<token> tokenize(std::string &s, bool eval)
|
||||||
{
|
{
|
||||||
std::vector<token> tokens;
|
std::vector<token> tokens;
|
||||||
std::string scratch;
|
std::string scratch;
|
||||||
bool quoted = false; // found a quote character ("" creates a token)
|
|
||||||
|
|
||||||
|
|
||||||
%%machine tokenizer;
|
%%machine tokenizer;
|
||||||
|
@ -241,15 +284,23 @@ std::vector<token> tokenize(const std::string &s, bool eval)
|
||||||
|
|
||||||
%%write exec;
|
%%write exec;
|
||||||
|
|
||||||
if (cs == tokenizer_error) {
|
if (!scratch.empty()) {
|
||||||
throw std::runtime_error("MPW Shell - Lexer error.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scratch.empty() || quoted) {
|
|
||||||
tokens.emplace_back(std::move(scratch));
|
tokens.emplace_back(std::move(scratch));
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// re-build s.
|
||||||
|
s.clear();
|
||||||
|
for (const token &t : tokens) {
|
||||||
|
s.append(t.string);
|
||||||
|
s.push_back(' ');
|
||||||
|
}
|
||||||
|
if (!s.empty()) s.pop_back();
|
||||||
|
|
||||||
|
for (token &t : tokens) {
|
||||||
|
if (t.type == token::text) unquote(t);
|
||||||
|
}
|
||||||
|
|
||||||
// alternate operator tokens for eval
|
// alternate operator tokens for eval
|
||||||
if (eval) {
|
if (eval) {
|
||||||
|
|
||||||
|
|
453
mpw-shell.cpp
453
mpw-shell.cpp
|
@ -2,6 +2,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <atomic>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -9,13 +11,15 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
|
#include "mpw_parser.h"
|
||||||
|
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
|
|
||||||
#include "phase1.h"
|
#include "macroman.h"
|
||||||
#include "phase2.h"
|
|
||||||
#include "command.h"
|
|
||||||
|
|
||||||
#include "cxx/mapped_file.h"
|
#include "cxx/mapped_file.h"
|
||||||
#include "cxx/filesystem.h"
|
#include "cxx/filesystem.h"
|
||||||
|
@ -23,33 +27,64 @@
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include <editline/readline.h>
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
|
#include <paths.h>
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
namespace fs = filesystem;
|
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() {
|
fs::path root() {
|
||||||
|
|
||||||
static fs::path root;
|
static fs::path root;
|
||||||
|
bool init = false;
|
||||||
|
|
||||||
if (root.empty()) {
|
static std::array<filesystem::path, 2> locations = { {
|
||||||
const char *cp = getenv("HOME");
|
"/usr/share/mpw/",
|
||||||
if (!cp || !*cp) {
|
"/usr/local/share/mpw/"
|
||||||
auto pw = getpwuid(getuid());
|
} };
|
||||||
if (!pw) {
|
|
||||||
fprintf(stderr,"Unable to determine home directory\n.");
|
if (!init) {
|
||||||
exit(EX_NOUSER);
|
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;
|
for (fs::path p : locations) {
|
||||||
//if (root.back() != '/') root.push_back('/');
|
p /= "mpw/";
|
||||||
//root += "mpw/";
|
if (fs::is_directory(p, ec)) {
|
||||||
root /= "mpw/";
|
root = std::move(p);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "### Warning: Unable to find mpw directory.\n");
|
||||||
}
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
@ -57,61 +92,86 @@ fs::path root() {
|
||||||
void init(Environment &env) {
|
void init(Environment &env) {
|
||||||
|
|
||||||
env.set("mpw", root());
|
env.set("mpw", root());
|
||||||
env.set("status", std::string("0"));
|
env.set("status", 0);
|
||||||
env.set("exit", std::string("1")); // terminate script on error.
|
env.set("exit", 1); // terminate script on error.
|
||||||
env.set("echo", std::string("1"));
|
env.set("echo", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int read_file(phase1 &p, const std::string &file) {
|
|
||||||
const mapped_file mf(file, mapped_file::readonly);
|
|
||||||
|
|
||||||
p.process(mf.begin(), mf.end(), false);
|
int read_file(Environment &e, const std::string &file, const fdmask &fds) {
|
||||||
p.finish();
|
std::error_code ec;
|
||||||
return 0;
|
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];
|
unsigned char buffer[2048];
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
|
|
||||||
for (;;) {
|
mpw_parser p(e, fds);
|
||||||
size = read(fd, buffer, sizeof(buffer));
|
e.status(0, false);
|
||||||
if (size == 0) break;
|
|
||||||
if (size < 0) {
|
|
||||||
if (errno == EINTR) continue;
|
|
||||||
perror("read: ");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
p.process(buffer, buffer + size);
|
|
||||||
} catch(std::exception &ex) {
|
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
|
||||||
p.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
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();
|
p.finish();
|
||||||
} catch(std::exception &ex) {
|
} catch(const execution_of_input_terminated &ex) {
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
return ex.status();
|
||||||
p.reset();
|
|
||||||
}
|
}
|
||||||
|
return e.status();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds);
|
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 out[2];
|
||||||
int ok;
|
int ok;
|
||||||
|
|
||||||
|
|
||||||
env.set("echo", "1");
|
env.set("echo", 1);
|
||||||
env.set("exit", "1");
|
env.set("exit", 1);
|
||||||
|
|
||||||
ok = pipe(out);
|
ok = pipe(out);
|
||||||
if (ok < 0) {
|
if (ok < 0) {
|
||||||
|
@ -136,9 +196,9 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
||||||
}
|
}
|
||||||
|
|
||||||
close(out[1]);
|
close(out[1]);
|
||||||
int rv = read_fd(p1, out[0]);
|
int rv = read_fd(env, out[0]);
|
||||||
close(out[0]);
|
close(out[0]);
|
||||||
p2.finish();
|
|
||||||
|
|
||||||
// check for make errors.
|
// check for make errors.
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
@ -164,20 +224,21 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
||||||
exit(EX_OSERR);
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return env.status();
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile int control_c = 0;
|
std::atomic<int> control_c{0};
|
||||||
void control_c_handler(int signal, siginfo_t *sinfo, void *context) {
|
void control_c_handler(int signal, siginfo_t *sinfo, void *context) {
|
||||||
|
|
||||||
// libedit gobbles up the first control-C and doesn't return until the second.
|
// libedit gobbles up the first control-C and doesn't return until the second.
|
||||||
// GNU's readline may return no the first.
|
// GNU's readline may return on the first.
|
||||||
if (control_c > 3) abort();
|
if (control_c > 3) abort();
|
||||||
control_c++;
|
++control_c;
|
||||||
//fprintf(stderr, "interrupt!\n");
|
//fprintf(stderr, "interrupt!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int interactive(Environment &env, phase1 &p, phase2& p2) {
|
|
||||||
|
int interactive(Environment &env) {
|
||||||
|
|
||||||
std::string history_file = root();
|
std::string history_file = root();
|
||||||
history_file += ".history";
|
history_file += ".history";
|
||||||
|
@ -194,16 +255,18 @@ int interactive(Environment &env, phase1 &p, phase2& p2) {
|
||||||
|
|
||||||
sigaction(SIGINT, &act, &old_act);
|
sigaction(SIGINT, &act, &old_act);
|
||||||
|
|
||||||
|
mpw_parser p(env, true);
|
||||||
|
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
const char *prompt = "# ";
|
const char *prompt = "# ";
|
||||||
if (p2.continuation()) prompt = "> ";
|
if (p.continuation()) prompt = "> ";
|
||||||
char *cp = readline(prompt);
|
char *cp = readline(prompt);
|
||||||
if (!cp) {
|
if (!cp) {
|
||||||
if (control_c) {
|
if (control_c) {
|
||||||
control_c = 0;
|
control_c = 0;
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
p.abort();
|
p.abort();
|
||||||
p2.abort();
|
|
||||||
env.status(-9, false);
|
env.status(-9, false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -213,47 +276,52 @@ int interactive(Environment &env, phase1 &p, phase2& p2) {
|
||||||
std::string s(cp);
|
std::string s(cp);
|
||||||
free(cp);
|
free(cp);
|
||||||
|
|
||||||
if (s.empty()) continue;
|
//if (s.empty()) continue;
|
||||||
|
|
||||||
// don't add if same as previous entry.
|
// don't add if same as previous entry.
|
||||||
HIST_ENTRY *he = history_get(history_length);
|
if (!s.empty()) {
|
||||||
if (he == nullptr || s != he->line)
|
HIST_ENTRY *he = history_get(history_length);
|
||||||
add_history(s.c_str());
|
if (he == nullptr || s != he->line)
|
||||||
|
add_history(s.c_str());
|
||||||
|
}
|
||||||
|
if (utf8)
|
||||||
|
s = utf8_to_macroman(s);
|
||||||
|
|
||||||
s.push_back('\n');
|
s.push_back('\n');
|
||||||
try {
|
|
||||||
p.process(s);
|
|
||||||
|
|
||||||
} catch(std::exception &ex) {
|
p.parse(s);
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
|
||||||
p.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
p.finish();
|
||||||
try {
|
|
||||||
p.finish();
|
|
||||||
} catch(std::exception &ex) {
|
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
|
||||||
p.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
sigaction(SIGINT, &old_act, nullptr);
|
sigaction(SIGINT, &old_act, nullptr);
|
||||||
|
|
||||||
write_history(history_file.c_str());
|
write_history(history_file.c_str());
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void help() {
|
void help() {
|
||||||
|
|
||||||
|
#undef _
|
||||||
|
#define _(x) puts(x)
|
||||||
|
|
||||||
|
_("MPW Shell " VERSION " (" VERSION_DATE ")");
|
||||||
|
_("mpw-shell [option...]");
|
||||||
|
_(" -c string # read commands from string");
|
||||||
|
_(" -d name[=value] # define variable name");
|
||||||
|
_(" -f # don't load MPW:Startup file");
|
||||||
|
_(" -h # display help information");
|
||||||
|
_(" -v # be verbose (echo = 1)");
|
||||||
|
|
||||||
|
#undef _
|
||||||
}
|
}
|
||||||
|
|
||||||
void define(Environment &env, const std::string &s) {
|
void define(Environment &env, const std::string &s) {
|
||||||
|
|
||||||
auto pos = s.find('=');
|
auto pos = s.find('=');
|
||||||
if (pos == s.npos) env.set(s, "1");
|
if (pos == s.npos) env.set(s, 1);
|
||||||
else {
|
else {
|
||||||
std::string k = s.substr(0, pos);
|
std::string k = s.substr(0, pos);
|
||||||
std::string v = s.substr(pos+1);
|
std::string v = s.substr(pos+1);
|
||||||
|
@ -262,14 +330,10 @@ void define(Environment &env, const std::string &s) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string basename(const char *cp) {
|
/*
|
||||||
std::string tmp(cp);
|
*
|
||||||
auto pos = tmp.rfind('/');
|
* todo: prevent -r and -s (don't generate shell code)
|
||||||
if (pos == tmp.npos) return tmp;
|
*/
|
||||||
return tmp.substr(pos+1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void make_help(void) {
|
void make_help(void) {
|
||||||
|
|
||||||
#undef _
|
#undef _
|
||||||
|
@ -305,60 +369,92 @@ int make(int argc, char **argv) {
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
args.reserve(argc+1);
|
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) {
|
args.push_back(""); // place-holder.
|
||||||
std::string tmp(*iter);
|
|
||||||
|
|
||||||
if (!__) {
|
while ((c = getopt_long(argc, argv, "d:ef:i:prstuvwy", longopts, nullptr)) != -1) {
|
||||||
if (tmp == "--")
|
std::string flag = "-"; flag.push_back(c);
|
||||||
{ __ = true; continue; }
|
switch(c) {
|
||||||
|
default:
|
||||||
|
make_help();
|
||||||
|
return EX_USAGE;
|
||||||
|
|
||||||
if (tmp == "--help")
|
case 'h':
|
||||||
{ make_help(); exit(0); }
|
make_help();
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (tmp == "--test" || tmp == "--dry-run")
|
case 1:
|
||||||
{ e.set("test", "1"); continue; }
|
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;
|
argc -= optind;
|
||||||
phase2 p2;
|
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;
|
|
||||||
if (!(ptr->terminal() && ++iter == v.end())) {
|
|
||||||
fprintf(stderr, "%s\n", ex.what());
|
|
||||||
}
|
|
||||||
e.status(ex.status(), false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
e.startup(true);
|
e.startup(true);
|
||||||
read_file(p1, root() / "Startup");
|
read_file(e, root() / "Startup");
|
||||||
e.startup(false);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,8 +464,9 @@ fs::path mpw_path() {
|
||||||
|
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
const char *cp = getenv("PATH");
|
||||||
std::string s(getenv("PATH"));
|
if (!cp) cp = _PATH_DEFPATH;
|
||||||
|
std::string s(cp);
|
||||||
string_splitter ss(s, ':');
|
string_splitter ss(s, ':');
|
||||||
for (; ss; ++ss) {
|
for (; ss; ++ss) {
|
||||||
if (ss->empty()) continue;
|
if (ss->empty()) continue;
|
||||||
|
@ -406,21 +503,48 @@ fs::path mpw_path() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init_locale() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libedit assumes utf-8 if locale is C.
|
||||||
|
* MacRoman is en_US. utf-8 is en_US.UTF8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const char *lang = getenv("LANG");
|
||||||
|
/*
|
||||||
|
if (lang && !strcmp(lang, "en_US")) {
|
||||||
|
setlocale(LC_ALL, "POSIX");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
utf8 = false;
|
||||||
|
if (lang && strcasestr(lang, ".UTF-8")) {
|
||||||
|
utf8 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
|
||||||
|
init_locale();
|
||||||
|
|
||||||
mpw_path();
|
mpw_path();
|
||||||
|
|
||||||
std::string self = basename(argv[0]);
|
fs::path self = fs::path(argv[0]).filename();
|
||||||
if (self == "mpw-make") return make(argc - 1, argv + 1);
|
if (self == "mpw-make") return make(argc, argv);
|
||||||
if (self == "mpw-shell" && argc > 1 && !strcmp(argv[1],"make")) return make(argc - 2, argv + 2);
|
if (self == "mpw-shell" && argc > 1 && !strcmp(argv[1],"make")) {
|
||||||
|
argv[1] = (char *)"mpw-make";
|
||||||
|
return make(argc - 1, argv + 1);
|
||||||
|
}
|
||||||
|
|
||||||
Environment e;
|
Environment e;
|
||||||
init(e);
|
init(e);
|
||||||
|
|
||||||
const char *cflag = nullptr;
|
const char *cflag = nullptr;
|
||||||
|
bool fflag = false;
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "c:D:v:h")) != -1) {
|
while ((c = getopt(argc, argv, "c:D:vhf")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
// -c command
|
// -c command
|
||||||
|
@ -434,6 +558,9 @@ int main(int argc, char **argv) {
|
||||||
// -v verbose
|
// -v verbose
|
||||||
e.set("echo", "1");
|
e.set("echo", "1");
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
fflag = true;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
help();
|
help();
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -448,52 +575,38 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!cflag) fprintf(stdout, "MPW Shell " VERSION "\n");
|
||||||
|
if (!fflag) {
|
||||||
|
fs::path startup = root() / "Startup";
|
||||||
|
e.startup(true);
|
||||||
|
mpw_parser p(e);
|
||||||
|
|
||||||
|
try {
|
||||||
phase1 p1;
|
read_file(e, startup);
|
||||||
phase2 p2;
|
} catch (const std::system_error &ex) {
|
||||||
|
fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what());
|
||||||
p1 >>= [&p2](std::string &&s) {
|
} catch (const quit_command_t &) {
|
||||||
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 0.1\n");
|
e.startup(false);
|
||||||
e.startup(true);
|
|
||||||
read_file(p1, root() / "Startup");
|
|
||||||
e.startup(false);
|
|
||||||
|
|
||||||
if (cflag) {
|
|
||||||
std::string s(cflag);
|
|
||||||
s.push_back('\n');
|
|
||||||
p1.process(s, true);
|
|
||||||
p2.finish();
|
|
||||||
exit(e.status());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isatty(STDIN_FILENO))
|
try {
|
||||||
interactive(e, p1, p2);
|
|
||||||
else
|
|
||||||
read_fd(p1, STDIN_FILENO);
|
|
||||||
p2.finish();
|
|
||||||
|
|
||||||
exit(e.status());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
mpw-shell.h
12
mpw-shell.h
|
@ -8,6 +8,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
#include "fdset.h"
|
||||||
|
|
||||||
const unsigned char escape = 0xb6;
|
const unsigned char escape = 0xb6;
|
||||||
|
|
||||||
|
@ -48,8 +49,8 @@ public:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<token> tokenize(const std::string &s, bool eval = false);
|
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(std::string &&s);
|
||||||
std::string quote(const 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
|
#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
|
|
@ -117,6 +117,12 @@ namespace {
|
||||||
write data;
|
write data;
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this is not quite right. dev:std* is a reference to the current output device.
|
||||||
|
* need to move this logic elsewhere.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine dev;
|
machine dev;
|
||||||
|
|
||||||
|
@ -149,7 +155,7 @@ namespace ToolBox
|
||||||
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
return str;
|
return std::move(str);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
207
phase1.cpp
Normal file
207
phase1.cpp
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
#include "phase1.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
st_text,
|
||||||
|
st_text_esc,
|
||||||
|
|
||||||
|
st_comment,
|
||||||
|
st_comment_esc,
|
||||||
|
|
||||||
|
st_vstring,
|
||||||
|
st_vstring_esc,
|
||||||
|
|
||||||
|
st_dstring,
|
||||||
|
st_dstring_esc,
|
||||||
|
|
||||||
|
st_sstring,
|
||||||
|
st_sstring_esc,
|
||||||
|
|
||||||
|
st_estring,
|
||||||
|
st_estring1,
|
||||||
|
st_estring1_esc,
|
||||||
|
|
||||||
|
st_estring2,
|
||||||
|
st_estring2_esc,
|
||||||
|
|
||||||
|
st_estring3,
|
||||||
|
|
||||||
|
st_fstring,
|
||||||
|
st_fstring_esc,
|
||||||
|
|
||||||
|
st_bstring,
|
||||||
|
st_bstring_esc,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int phase1::process(unsigned char c, int st) {
|
||||||
|
|
||||||
|
const unsigned char esc = 0xb6;
|
||||||
|
|
||||||
|
|
||||||
|
if (c == '\r' || c == '\n') {
|
||||||
|
switch (st) {
|
||||||
|
case st_text:
|
||||||
|
case st_comment:
|
||||||
|
default: // will error later.
|
||||||
|
flush();
|
||||||
|
multiline = false;
|
||||||
|
line++;
|
||||||
|
return st_text;
|
||||||
|
|
||||||
|
case st_comment_esc:
|
||||||
|
multiline = true;
|
||||||
|
line++;
|
||||||
|
return st_text;
|
||||||
|
|
||||||
|
case st_text_esc:
|
||||||
|
case st_vstring_esc:
|
||||||
|
case st_dstring_esc:
|
||||||
|
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++;
|
||||||
|
return st - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st != st_comment) scratch.push_back(c);
|
||||||
|
|
||||||
|
switch(st) {
|
||||||
|
|
||||||
|
case st_text:
|
||||||
|
text:
|
||||||
|
switch(c) {
|
||||||
|
case '#':
|
||||||
|
scratch.pop_back();
|
||||||
|
return st_comment;
|
||||||
|
case esc:
|
||||||
|
return st_text_esc;
|
||||||
|
case '{':
|
||||||
|
return st_vstring;
|
||||||
|
case '"':
|
||||||
|
return st_dstring;
|
||||||
|
case '\'':
|
||||||
|
return st_sstring;
|
||||||
|
case '`':
|
||||||
|
return st_estring;
|
||||||
|
case '/':
|
||||||
|
return st_fstring;
|
||||||
|
case '\\':
|
||||||
|
return st_bstring;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return st_text;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case st_comment:
|
||||||
|
if (c == esc) return st_comment_esc;
|
||||||
|
return st_comment;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case st_comment_esc:
|
||||||
|
case st_text_esc:
|
||||||
|
case st_dstring_esc:
|
||||||
|
case st_estring1_esc:
|
||||||
|
case st_estring2_esc:
|
||||||
|
return st-1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case st_sstring_esc:
|
||||||
|
// fall through
|
||||||
|
case st_sstring:
|
||||||
|
if (c == '\'') return st_text;
|
||||||
|
if (c == esc) return st_sstring_esc;
|
||||||
|
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;
|
||||||
|
return st_dstring;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case st_vstring_esc:
|
||||||
|
// fall through
|
||||||
|
case st_vstring:
|
||||||
|
// '{' var '}' or '{{' var '}}'
|
||||||
|
// don't care if {{ or { at this point. A single } terminates.
|
||||||
|
if (c == '}') return st_text;
|
||||||
|
if (c == esc) return st_vstring_esc;
|
||||||
|
return st_vstring;
|
||||||
|
|
||||||
|
case st_estring:
|
||||||
|
// ``...`` or `...`
|
||||||
|
if (c == '`') return st_estring2;
|
||||||
|
// fall through.
|
||||||
|
case st_estring1:
|
||||||
|
if (c == '`') return st_text;
|
||||||
|
if (c == esc) return st_estring1_esc;
|
||||||
|
return st_estring1;
|
||||||
|
|
||||||
|
case st_estring2:
|
||||||
|
if (c == '`') return st_estring3;
|
||||||
|
if (c == esc) return st_estring2_esc;
|
||||||
|
return st_estring2;
|
||||||
|
|
||||||
|
case st_estring3:
|
||||||
|
if (c == '`') return st_text;
|
||||||
|
// error! handled later.
|
||||||
|
goto text;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
assert(!"unknown state");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void phase1::parse(const unsigned char *begin, const unsigned char *end) {
|
||||||
|
while (begin != end) {
|
||||||
|
cs = process(*begin++, cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void phase1::finish() {
|
||||||
|
|
||||||
|
cs = process('\n', cs);
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void phase1::reset() {
|
||||||
|
cs = st_text;
|
||||||
|
multiline = false;
|
||||||
|
line = 1;
|
||||||
|
scratch.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void phase1::flush() {
|
||||||
|
multiline = false;
|
||||||
|
if (scratch.empty()) return;
|
||||||
|
if (_then) _then(std::move(scratch));
|
||||||
|
scratch.clear();
|
||||||
|
}
|
38
phase1.h
38
phase1.h
|
@ -8,41 +8,33 @@
|
||||||
class phase1 {
|
class phase1 {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(std::string &&)> pipe_function;
|
|
||||||
|
|
||||||
phase1();
|
typedef std::function<void(std::string &&)> next_function_type;
|
||||||
|
|
||||||
void process(const unsigned char *begin, const unsigned char *end, bool final = false);
|
phase1() = default;
|
||||||
|
|
||||||
void process(const char *begin, const char *end, bool final = false) {
|
void parse(const unsigned char *begin, const unsigned char *end);
|
||||||
process((const unsigned char *)begin, (const unsigned char *)end, final);
|
void parse(const std::string &s) { parse((const unsigned char *)s.data(), (const unsigned char *)s.data() + s.size()); }
|
||||||
}
|
void finish();
|
||||||
|
|
||||||
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 reset();
|
void 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void abort() { reset(); }
|
void abort() { reset(); }
|
||||||
|
|
||||||
|
bool continuation() const { return multiline; }
|
||||||
|
|
||||||
|
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
int process(unsigned char, int);
|
||||||
|
void flush();
|
||||||
|
|
||||||
std::string scratch;
|
std::string scratch;
|
||||||
pipe_function pipe_to;
|
|
||||||
int line = 1;
|
int line = 1;
|
||||||
int cs = 0;
|
int cs = 0;
|
||||||
|
bool multiline = false;
|
||||||
|
|
||||||
|
next_function_type _then;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
75
phase2.h
75
phase2.h
|
@ -3,94 +3,41 @@
|
||||||
#define __phase2_h__
|
#define __phase2_h__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
#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 {
|
class phase2 {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(command_ptr_vector &&)> pipe_function;
|
|
||||||
|
|
||||||
phase2();
|
typedef std::function<void(int, std::string &&)> next_function_type;
|
||||||
phase2(const phase2 &) = delete;
|
|
||||||
phase2(phase2 &&) = default;
|
|
||||||
|
|
||||||
phase2 & operator=(const phase2 &) = delete;
|
phase2() = default;
|
||||||
phase2 & operator=(phase2 &&) = default;
|
|
||||||
|
|
||||||
void operator()(const std::string &line) { process(line); }
|
void parse(std::string &&);
|
||||||
void process(const std::string &line);
|
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
phase2 &operator >>=(pipe_function f) { pipe_to = f; return *this; }
|
void reset();
|
||||||
|
void abort() { reset(); }
|
||||||
|
|
||||||
template<class F>
|
bool continuation() const { return false; }
|
||||||
phase2 &operator >>= (F &f) {
|
|
||||||
using std::placeholders::_1;
|
|
||||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool continuation() const {
|
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||||
return parser ? parser->continuation() : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void parse(int, std::string &&);
|
void parse(int type, std::string &&s);
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<phase2_parser> parser;
|
|
||||||
|
|
||||||
std::string scratch;
|
std::string scratch;
|
||||||
int type = 0;
|
int type = 0;
|
||||||
|
int pcount = 0;
|
||||||
|
|
||||||
pipe_function pipe_to;
|
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
bool special();
|
bool special();
|
||||||
void classify();
|
int classify();
|
||||||
void exec();
|
void exec();
|
||||||
|
|
||||||
|
next_function_type _then;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
405
phase2.rl
405
phase2.rl
|
@ -4,9 +4,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "phase2-parser.h"
|
|
||||||
#include "phase2.h"
|
#include "phase2.h"
|
||||||
#include "command.h"
|
#include "phase3.h"
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine main;
|
machine main;
|
||||||
|
@ -14,288 +13,284 @@
|
||||||
|
|
||||||
action not_special { !special() }
|
action not_special { !special() }
|
||||||
|
|
||||||
|
action parse_ws {
|
||||||
|
if (scratch.empty()) fgoto main;
|
||||||
|
}
|
||||||
|
action parse_semi {
|
||||||
|
flush();
|
||||||
|
parse(SEMI, ";");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
action parse_amp_amp {
|
||||||
|
if (!special()) {
|
||||||
|
scratch.pop_back();
|
||||||
|
flush();
|
||||||
|
parse(AMP_AMP, "&&");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_pipe_pipe {
|
||||||
|
if (!special()) {
|
||||||
|
scratch.pop_back();
|
||||||
|
flush();
|
||||||
|
parse(PIPE_PIPE, "||");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_pipe_any {
|
||||||
|
if (!special()) {
|
||||||
|
scratch.pop_back();
|
||||||
|
flush();
|
||||||
|
parse(PIPE, "|");
|
||||||
|
}
|
||||||
|
fhold;
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_pipe_eof {
|
||||||
|
|
||||||
|
if (!special()) {
|
||||||
|
scratch.pop_back();
|
||||||
|
flush();
|
||||||
|
parse(PIPE, "|");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
action parse_lparen {
|
||||||
|
if (scratch.empty()) {
|
||||||
|
parse(LPAREN, "(");
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
pcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
action parse_rparen {
|
||||||
|
if (pcount <= 0) {
|
||||||
|
flush();
|
||||||
|
// parse(RPAREN, ")");
|
||||||
|
scratch.push_back(fc);
|
||||||
|
fgoto main;
|
||||||
|
}
|
||||||
|
--pcount;
|
||||||
|
}
|
||||||
|
|
||||||
escape = 0xb6;
|
escape = 0xb6;
|
||||||
ws = [ \t];
|
ws = [ \t];
|
||||||
|
|
||||||
|
|
||||||
escape_seq =
|
escape_seq = escape any ;
|
||||||
escape any
|
|
||||||
;
|
|
||||||
|
|
||||||
sstring =
|
schar = [^'];
|
||||||
[']
|
sstring = ['] schar** ['] ;
|
||||||
( (any-[']) )*
|
|
||||||
[']
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - 's must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
vstring =
|
fchar = [^/];
|
||||||
[{]
|
fstring = [/] fchar** [/] ;
|
||||||
( (any-[}]) )*
|
|
||||||
[}]
|
bchar = [^\\];
|
||||||
$err{
|
bstring = [\\] bchar** [\\] ;
|
||||||
throw std::runtime_error("### MPW Shell - {s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
vchar = [^}];
|
||||||
|
vstring = [{] vchar** [}] ;
|
||||||
|
|
||||||
# double-quoted string.
|
# double-quoted string.
|
||||||
dstring =
|
dchar = escape_seq | (any - escape - ["]) ;
|
||||||
["]
|
dstring = ["] dchar** ["];
|
||||||
(
|
|
||||||
escape_seq
|
|
||||||
|
|
|
||||||
(any-escape-["])
|
|
||||||
)*
|
|
||||||
["]
|
|
||||||
$err{
|
|
||||||
throw std::runtime_error("### MPW Shell - \"s must occur in pairs.");
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
|
echar = escape_seq | (any - escape - [`]) ;
|
||||||
|
estring1 = '`' echar** '`';
|
||||||
|
estring2 = '``' echar** '``';
|
||||||
|
estring = estring1 | estring2 ;
|
||||||
|
|
||||||
main := |*
|
# default action is to push character into scratch.
|
||||||
|
# fgoto main inhibits.
|
||||||
|
main := (
|
||||||
|
ws $parse_ws
|
||||||
|
| ';' $parse_semi
|
||||||
|
| '(' $parse_lparen
|
||||||
|
| ')' $parse_rparen
|
||||||
|
| '|' <eof(parse_pipe_eof)
|
||||||
|
| '|' [^|] $parse_pipe_any
|
||||||
|
| '|' '|' $parse_pipe_pipe
|
||||||
|
| '&' '&' $parse_amp_amp
|
||||||
|
| escape_seq
|
||||||
|
| sstring
|
||||||
|
| fstring
|
||||||
|
| bstring
|
||||||
|
| dstring
|
||||||
|
| vstring
|
||||||
|
| estring
|
||||||
|
| any
|
||||||
|
)** ${ scratch.push_back(fc); };
|
||||||
|
|
||||||
'||' when not_special => {
|
|
||||||
flush();
|
|
||||||
parse(PIPE_PIPE, std::string(ts, te));
|
|
||||||
};
|
|
||||||
|
|
||||||
'&&' when not_special => {
|
|
||||||
flush();
|
|
||||||
parse(AMP_AMP, std::string(ts, te));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
# ( evaluate (1+2) ) is lparen, eval, rparen.
|
|
||||||
# need to balance parens here and terminate a special token when it goes negative.
|
|
||||||
|
|
||||||
|
|
||||||
'(' => {
|
|
||||||
if (special()) {
|
|
||||||
pcount++;
|
|
||||||
scratch.push_back(fc);
|
|
||||||
} else {
|
|
||||||
flush();
|
|
||||||
parse(LPAREN, std::string(ts, te));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
')' => {
|
|
||||||
if (special() && pcount-- > 0) scratch.push_back(fc);
|
|
||||||
else {
|
|
||||||
flush();
|
|
||||||
scratch.push_back(fc);
|
|
||||||
type = RPAREN;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
';' => { flush(); parse(SEMI, ";"); };
|
|
||||||
|
|
||||||
ws => { if (!scratch.empty()) scratch.push_back(fc); };
|
|
||||||
|
|
||||||
sstring => { scratch.append(ts, te); };
|
|
||||||
dstring => { scratch.append(ts, te); };
|
|
||||||
vstring => { scratch.append(ts, te); };
|
|
||||||
escape_seq => { scratch.append(ts, te); };
|
|
||||||
|
|
||||||
|
|
||||||
(any-escape-['"{]) => { scratch.push_back(fc); };
|
|
||||||
*|;
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine classify;
|
machine argv0;
|
||||||
alphtype unsigned char;
|
alphtype unsigned char;
|
||||||
|
|
||||||
|
action push { argv0.push_back(tolower(fc)); }
|
||||||
|
action break { fbreak; }
|
||||||
|
|
||||||
|
escape = 0xb6;
|
||||||
ws = [ \t];
|
ws = [ \t];
|
||||||
|
|
||||||
IF = /if/i;
|
|
||||||
ELSE = /else/i;
|
# ` and { not supported here.
|
||||||
END = /end/i;
|
|
||||||
BEGIN = /begin/i;
|
|
||||||
EVALUATE = /evaluate/i;
|
|
||||||
|
|
||||||
|
|
||||||
main := |*
|
# hmmm ... only push the converted char - escape n = \n, for example.
|
||||||
IF %eof{ type = IF; return; };
|
esc_seq =
|
||||||
IF ws => { type = IF; return; };
|
escape (
|
||||||
|
'f' ${argv0.push_back('\f'); } |
|
||||||
|
'n' ${argv0.push_back('\n'); } |
|
||||||
|
't' ${argv0.push_back('\t'); } |
|
||||||
|
[^fnt] $push
|
||||||
|
);
|
||||||
|
|
||||||
ELSE %eof{ type = ELSE; return; };
|
schar = [^'] $push;
|
||||||
ELSE ws => { type = ELSE; return; };
|
sstring = ['] schar** ['];
|
||||||
|
|
||||||
ELSE ws+ IF %eof{ type = ELSE_IF; return; };
|
dchar = esc_seq | (any-escape-["]) $push;
|
||||||
ELSE ws+ IF ws => { type = ELSE_IF; return; };
|
dstring = ["] dchar** ["];
|
||||||
|
|
||||||
EVALUATE %eof{ type = EVALUATE; return; };
|
# mpw doesn't handle quotes at this point,
|
||||||
EVALUATE ws => { type = EVALUATE; return; };
|
# so simplify and stop if we see anything invalid.
|
||||||
|
main := (
|
||||||
END %eof{ type = END; return; };
|
ws $break
|
||||||
END ws => { type = END; return; };
|
| [|<>] $break
|
||||||
|
| 0xb7 $break
|
||||||
BEGIN %eof{ type = BEGIN; return; };
|
| 0xb3 $break
|
||||||
BEGIN ws => { type = BEGIN; return; };
|
| [^a-zA-Z] ${ return COMMAND; }
|
||||||
|
| any $push
|
||||||
'(' => { type = LPAREN; return; };
|
)**;
|
||||||
*|;
|
|
||||||
|
|
||||||
}%%
|
}%%
|
||||||
|
|
||||||
|
|
||||||
|
int phase2::classify() {
|
||||||
|
|
||||||
|
%%machine argv0;
|
||||||
|
%%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();
|
||||||
|
const unsigned char *pe = p + scratch.size();
|
||||||
|
int cs;
|
||||||
|
|
||||||
|
type = COMMAND;
|
||||||
|
|
||||||
|
%%write init;
|
||||||
|
%%write exec;
|
||||||
|
|
||||||
|
// fprintf(stderr, "%s -> %s\n", scratch.c_str(), argv0.c_str());
|
||||||
|
#undef _
|
||||||
|
#define _(a,b) if (argv0 == a) { type = b; return type; }
|
||||||
|
|
||||||
|
// expand aliases?
|
||||||
|
|
||||||
|
_("begin", BEGIN)
|
||||||
|
_("break", BREAK)
|
||||||
|
_("continue", CONTINUE)
|
||||||
|
_("else", ELSE)
|
||||||
|
_("end", END)
|
||||||
|
_("evaluate", EVALUATE)
|
||||||
|
_("exit", EXIT)
|
||||||
|
_("for", FOR)
|
||||||
|
_("if", IF)
|
||||||
|
_("loop", LOOP)
|
||||||
|
|
||||||
|
#undef _
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
%% machine classify;
|
%% machine argv0;
|
||||||
%% write data;
|
%% write data;
|
||||||
|
|
||||||
%% machine main;
|
%% machine main;
|
||||||
%% write data;
|
%% write data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::flush() {
|
void phase2::flush() {
|
||||||
|
//fprintf(stderr, "flush: %s\n", scratch.c_str());
|
||||||
// remove white space...
|
// remove white space...
|
||||||
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
||||||
|
|
||||||
|
|
||||||
if (!scratch.empty()) {
|
if (!scratch.empty()) parse(classify(), std::move(scratch));
|
||||||
if (!type) classify();
|
|
||||||
parse(type, std::move(scratch));
|
|
||||||
}
|
|
||||||
|
|
||||||
type = 0;
|
type = 0;
|
||||||
|
pcount = 0;
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* slightly wrong since whitespace is needed for it to be special. */
|
/* slightly wrong since whitespace is needed for it to be special. */
|
||||||
bool phase2::special() {
|
bool phase2::special() {
|
||||||
if (!type) classify();
|
|
||||||
|
|
||||||
switch (type) {
|
switch (classify()) {
|
||||||
case IF:
|
case IF:
|
||||||
case ELSE:
|
case ELSE:
|
||||||
case ELSE_IF:
|
case ELSE_IF:
|
||||||
case EVALUATE:
|
case EVALUATE:
|
||||||
|
case BREAK:
|
||||||
|
case CONTINUE:
|
||||||
|
case EXIT:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::classify() {
|
void phase2::parse(int type, std::string &&s) {
|
||||||
if (type) return;
|
if (_then) _then(type, std::move(s));
|
||||||
if (scratch.empty()) return;
|
|
||||||
|
|
||||||
int cs;
|
|
||||||
int act;
|
|
||||||
const unsigned char *p = (const unsigned char *)scratch.data();
|
|
||||||
const unsigned char *pe = p + scratch.size();
|
|
||||||
const unsigned char *eof = pe;
|
|
||||||
const unsigned char *te, *ts;
|
|
||||||
|
|
||||||
type = COMMAND;
|
|
||||||
|
|
||||||
%% machine classify;
|
|
||||||
%% write init;
|
|
||||||
%% write exec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::process(const std::string &line) {
|
void phase2::parse(std::string &&line) {
|
||||||
|
|
||||||
if (line.empty()) { finish(); return; }
|
//fprintf(stderr, "-> %s\n", line.c_str());
|
||||||
|
|
||||||
int pcount = 0; // special form parens cannot cross lines.
|
|
||||||
|
|
||||||
int cs;
|
int cs;
|
||||||
int act;
|
|
||||||
const unsigned char *p = (const unsigned char *)line.data();
|
const unsigned char *p = (const unsigned char *)line.data();
|
||||||
const unsigned char *pe = p + line.size();
|
const unsigned char *pe = p + line.size();
|
||||||
const unsigned char *eof = pe;
|
const unsigned char *eof = pe;
|
||||||
const unsigned char *te, *ts;
|
|
||||||
|
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
type = 0;
|
type = 0;
|
||||||
|
pcount = 0; // parenthesis balancing within command only.
|
||||||
|
|
||||||
%% machine main;
|
%% machine main;
|
||||||
%% write init;
|
%% write init;
|
||||||
%% write exec;
|
%% write exec;
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
// 2 NLs to make the stack reduce. harmless if in a multi-line constuct.
|
if (_then) {
|
||||||
parse(NL, "");
|
_then(NL, "");
|
||||||
parse(NL, "");
|
_then(NL, "");
|
||||||
|
}
|
||||||
exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::finish() {
|
void phase2::finish() {
|
||||||
parse(0, "");
|
|
||||||
exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::parse(int token, std::string &&s) {
|
void phase2::reset() {
|
||||||
if (parser) parser->parse(token, std::move(s));
|
|
||||||
}
|
type = 0;
|
||||||
|
pcount = 0;
|
||||||
void phase2::exec() {
|
scratch.clear();
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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 {
|
||||||
|
|
||||||
#include "phase2.h"
|
#include "phase3_parser.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#define LEMON_SUPER phase2_parser
|
#define LEMON_SUPER phase3
|
||||||
#include "phase2-parser.h"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
%code {
|
%code {
|
||||||
|
|
||||||
std::unique_ptr<phase2_parser> phase2_parser::make() {
|
std::unique_ptr<phase3> phase3::make() {
|
||||||
return std::make_unique<yypParser>();
|
return std::make_unique<yypParser>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool phase2_parser::continuation() const {
|
bool phase3::continuation() const {
|
||||||
yypParser *self = (yypParser *)this;
|
yypParser *self = (yypParser *)this;
|
||||||
|
|
||||||
for (const auto &e : *self) {
|
for (const auto &e : *self) {
|
||||||
|
@ -27,10 +27,52 @@ bool phase2_parser::continuation() const {
|
||||||
if (e.major == IF) return true;
|
if (e.major == IF) return true;
|
||||||
if (e.major == AMP_AMP) return true;
|
if (e.major == AMP_AMP) return true;
|
||||||
if (e.major == PIPE_PIPE) return true;
|
if (e.major == PIPE_PIPE) return true;
|
||||||
|
if (e.major == LOOP) return true;
|
||||||
|
if (e.major == FOR) return true;
|
||||||
|
if (e.major == PIPE) return true;
|
||||||
|
if (e.major == PIPE_PIPE) return true;
|
||||||
|
if (e.major == AMP_AMP) return true;
|
||||||
}
|
}
|
||||||
return false;
|
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.
|
%left PIPE_PIPE AMP_AMP.
|
||||||
|
@ -64,13 +106,10 @@ command_list ::= command_list command(C) sep . {
|
||||||
|
|
||||||
|
|
||||||
compound_list ::= .
|
compound_list ::= .
|
||||||
compound_list(RV) ::= compound_list(L) sep. {
|
compound_list(L) ::= compound_list(L) sep.
|
||||||
RV = std::move(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
compound_list(RV) ::= compound_list(L) command(C) sep . {
|
compound_list(L) ::= compound_list(L) command(C) sep . {
|
||||||
RV = std::move(L);
|
if (C) L.emplace_back(std::move(C));
|
||||||
if (C) RV.emplace_back(std::move(C));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,19 +129,29 @@ command(RV) ::= command(L) AMP_AMP opt_nl command(R). {
|
||||||
RV = std::make_unique<and_command>(std::move(L), std::move(R));
|
RV = std::make_unique<and_command>(std::move(L), std::move(R));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
command(RV) ::= command(L) PIPE opt_nl command(R). {
|
||||||
command(RV) ::= command PIPE opt_nl command. {
|
|
||||||
RV = std::make_unique<pipe_command>(std::move(L), std::move(R));
|
RV = std::make_unique<pipe_command>(std::move(L), std::move(R));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
command(RV) ::= term(T). { RV = std::move(T); }
|
command(C) ::= term(C).
|
||||||
|
|
||||||
term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move(C)); }
|
term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move(C)); }
|
||||||
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
||||||
term(RV) ::= if_command(C). { RV = std::move(C); }
|
term(RV) ::= BREAK(C). { RV = std::make_unique<break_command>(std::move(C)); }
|
||||||
term(RV) ::= begin_command(C). { RV = std::move(C); }
|
term(RV) ::= CONTINUE(C). { RV = std::make_unique<continue_command>(std::move(C)); }
|
||||||
term(RV) ::= paren_command(C). { RV = 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).
|
||||||
|
term(C) ::= loop_command(C).
|
||||||
|
term(C) ::= for_command(C).
|
||||||
|
|
||||||
|
|
||||||
|
/* lexer error (mismatched quotes, etc) */
|
||||||
|
term(RV) ::= ERROR(C). {
|
||||||
|
RV = std::make_unique<error_command>(@C, std::move(C));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fall back to an end error. w/o fallback, it will cause a parse conflict.
|
* fall back to an end error. w/o fallback, it will cause a parse conflict.
|
||||||
|
@ -110,22 +159,26 @@ term(RV) ::= paren_command(C). { RV = std::move(C); }
|
||||||
/*
|
/*
|
||||||
%fallback ERROR END RPAREN ELSE ELSE_IF.
|
%fallback ERROR END RPAREN ELSE ELSE_IF.
|
||||||
|
|
||||||
term(RV) ::= ERROR(C). {
|
|
||||||
RV = std::make_unique<error_command>(@C, std::move(C));
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
term(RV) ::= error RPAREN.
|
||||||
|
term(RV) ::= error END.
|
||||||
|
term(RV) ::= LPAREN error RPAREN.
|
||||||
|
term(RV) ::= BEGIN error END.
|
||||||
|
term(RV) ::= IF error END.
|
||||||
|
term(RV) ::= LOOP error END.
|
||||||
|
term(RV) ::= FOR error END.
|
||||||
|
*/
|
||||||
|
|
||||||
/* compound list ends with a separator. paren command does not need the final separator */
|
/* compound list ends with a separator. paren command does not need the final separator */
|
||||||
%type paren_list { command_ptr_vector }
|
%type paren_list { command_ptr_vector }
|
||||||
|
|
||||||
paren_list(RV) ::= compound_list(L) . {
|
paren_list(L) ::= compound_list(L) .
|
||||||
RV = std::move(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
paren_list(RV) ::= compound_list(L) command(C) . {
|
paren_list(L) ::= compound_list(L) command(C) . {
|
||||||
RV = std::move(L);
|
L.emplace_back(std::move(C));
|
||||||
RV.emplace_back(std::move(C));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paren_command(RV) ::= LPAREN(T) paren_list(L) RPAREN(E). {
|
paren_command(RV) ::= LPAREN(T) paren_list(L) RPAREN(E). {
|
||||||
|
@ -137,6 +190,15 @@ begin_command(RV) ::= BEGIN(T) sep compound_list(L) END(E). {
|
||||||
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
|
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loop_command(RV) ::= LOOP(T) sep compound_list(L) END(E). {
|
||||||
|
RV = std::make_unique<loop_command>(@T, std::move(L), std::move(T), std::move(E));
|
||||||
|
}
|
||||||
|
|
||||||
|
for_command(RV) ::= FOR(T) sep compound_list(L) END(E). {
|
||||||
|
RV = std::make_unique<for_command>(@T, std::move(L), std::move(T), std::move(E));
|
||||||
|
}
|
||||||
|
|
||||||
if_command(RV) ::= IF(I) sep compound_list(L) END(E). {
|
if_command(RV) ::= IF(I) sep compound_list(L) END(E). {
|
||||||
|
|
||||||
if_command::clause_vector_type v;
|
if_command::clause_vector_type v;
|
||||||
|
@ -167,9 +229,8 @@ else_command(RV) ::= else(E) sep compound_list(L). {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
else_command(RV) ::= else_command(EC) else(E) sep compound_list(L). {
|
else_command(EC) ::= else_command(EC) else(E) sep compound_list(L). {
|
||||||
RV = std::move(EC);
|
EC.emplace_back(std::make_unique<if_else_clause>(@E, std::move(L), std::move(E)));
|
||||||
RV.emplace_back(std::make_unique<if_else_clause>(@E, std::move(L), std::move(E)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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
|
Loading…
Reference in New Issue
Block a user