Compare commits

...

26 Commits
0.4 ... master

Author SHA1 Message Date
Kelvin Sherlock 91590e92aa add missing (mostly macroman) operators 2022-11-25 16:02:27 -05:00
Kelvin Sherlock 6199944350 script parameters, move shift to the environment 2022-11-23 21:22:00 -05:00
Kelvin Sherlock f799eb6d81 allow break within loop ... end 2022-11-23 18:26:30 -05:00
Kelvin Sherlock 298c601300 include trailing text with the ) token so redirection works correctly 2022-11-23 18:18:32 -05:00
Kelvin Sherlock ce1a36eba5 add regular expression support for evaluate, if, etc.
eg:

evaluate abc =~ /(abc)®4/ # sets the '@4' environment variable.
evaluate abc !~ /[aA]bc/

MPW regular expressions are converted to c++11 std::regex regular expressions and evaluated.
Sadly, the // regular expression syntax interferes with unix-paths (if the / count is odd).  quoting or ∂-escaping the /s is therefore necessary.

file globbing is not yet implemented.
2022-11-22 17:22:49 -05:00
Kelvin Sherlock a90ca3c849 use classic macos file type to identify scripts. 2022-11-13 22:47:23 -05:00
Kelvin Sherlock fdf33c69b7 token support for regular expression strings /.../ and \...\. Also tokenizer support for =~ and !~ operators. 2022-11-02 21:42:25 -04:00
Kelvin Sherlock 6f2b59c4d6 script support. scripts run with an independent copy of the environment and aliases. local variables are not imported.
Currently, it for a ".text" extension to check if it's a script; this is a placeholder.
2022-11-02 21:42:25 -04:00
ksherlock fd94247aec
Merge pull request #6 from uliwitness/feature/docs-fixes
Add build instructions to Readme.
2022-10-16 12:12:08 -04:00
Uli Kusterer 8e54c6519c Add build instructions to Readme. 2022-10-16 14:39:56 +02:00
ksherlock 0c1419ec98
Update cmake-macos.yml 2022-09-25 13:19:26 -04:00
Kelvin Sherlock 225c3b8ebd open(O_CREATE) needs 3rd parameter. 2020-12-01 23:31:28 -05:00
ksherlock da24b85f68
Create cmake-ubuntu.yml 2020-12-01 23:21:37 -05:00
ksherlock f25a30edc7
Update cmake.yml 2020-12-01 23:08:34 -05:00
ksherlock e8c0080f77
Create cmake.yml 2020-12-01 23:05:43 -05:00
Kelvin Sherlock b8cada73e8 Merge branch 'master' of github.com:ksherlock/mpw-shell 2020-12-01 23:01:48 -05:00
Kelvin Sherlock be1c6c14fa std::move warning. 2020-12-01 23:01:00 -05:00
Kelvin Sherlock a15d2bf257 include generated lemon++ parser code so lemon++ isn't needed. 2020-12-01 23:00:41 -05:00
ksherlock 87b2cc0902
Update .travis.yml 2017-11-26 14:40:30 -05:00
Kelvin Sherlock 5d95f10dd8 remove trailing newline from sub-shell strings. 2017-11-26 14:02:59 -05:00
ksherlock 9d4340b3ac Merge pull request #4 from MaddTheSane/vsprintfFix
Fix messed up varargs call in environment
2017-05-01 18:06:04 -04:00
C.W. Betts 45df4524ea Fix messed up varargs calls. 2017-05-01 15:44:42 -06:00
Kelvin Sherlock 474b10ccaa look for ~/mpw, /usr/share/mpw, /usr/local/mpw 2016-10-08 09:55:31 -04:00
Kelvin Sherlock ed96470e18 builtin_help 2016-10-08 09:54:06 -04:00
Kelvin Sherlock 7b99997f28 Bump Version: 0.4 2016-09-24 12:51:24 -04:00
Kelvin Sherlock 42f9552352 nl @ end. 2016-09-24 12:49:10 -04:00
28 changed files with 3640 additions and 77 deletions

36
.github/workflows/cmake-macos.yml vendored Normal file
View 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
View 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

View File

@ -14,5 +14,5 @@ script: make
before_install:
- brew update
- brew install ragel
- brew install "https://github.com/ksherlock/lemon--/raw/master/lemon++.rb"
- brew install "https://raw.githubusercontent.com/ksherlock/homebrew-ksherlock/master/lemonxx.rb"

View File

@ -89,17 +89,28 @@ RAGEL_TARGET(value value.rl value.cpp COMPILE_FLAGS "-p -G2")
# need to copy all OUTPUT file to the build dir
add_custom_command(
OUTPUT phase3.cpp phase3.h phase3.out
COMMAND lemon++ phase3.lemon
COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/
MAIN_DEPENDENCY phase3.lemon
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# add_custom_command(
# OUTPUT phase3.cpp phase3.h phase3.out
# COMMAND lemon++ phase3.lemon
# COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/
# MAIN_DEPENDENCY phase3.lemon
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# )
find_program(LEMON_PLUSPLUS NAMES lemon++)
if (LEMON_PLUSPLUS)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/phase3.cpp ${CMAKE_CURRENT_SOURCE_DIR}/phase3.h
#COMMAND cp -f "${CMAKE_CURRENT_SOURCE_DIR}/parser.lemon" "parser.lemon"
COMMAND lemon++ phase3.lemon
MAIN_DEPENDENCY phase3.lemon
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
endif()
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
mpw-shell-parser.cpp mpw_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 phase3.cpp command.cpp environment.cpp builtins.cpp
pathnames.cpp
macroman.cpp

View File

@ -67,3 +67,24 @@ Command Line Flags
-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`.

View File

@ -17,9 +17,11 @@
#include <cstdarg>
#include <unistd.h>
#include <strings.h>
#include "cxx/string_splitter.h"
#include "cxx/filesystem.h"
#include "cxx/mapped_file.h"
//MAXPATHLEN
#include <sys/param.h>
@ -186,7 +188,8 @@ int builtin_shift(Environment &env, const std::vector<std::string> &tokens, cons
}
if (n == 0) return 0;
env.shift(n);
#if 0
auto argv = load_argv(env);
if (argv.empty()) return 0;
@ -197,7 +200,7 @@ int builtin_shift(Environment &env, const std::vector<std::string> &tokens, cons
} while (--n);
env.set_argv(argv);
#endif
return 0;
}
@ -689,7 +692,7 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
tokens.pop_back();
tokens.pop_back();
int32_t i = evaluate_expression("Evaluate", std::move(tokens));
int32_t i = evaluate_expression(env, "Evaluate", std::move(tokens));
switch(type) {
case '=':
@ -719,7 +722,7 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
}
}
int32_t i = evaluate_expression("Evaluate", std::move(tokens));
int32_t i = evaluate_expression(env, "Evaluate", std::move(tokens));
if (output == 'h') {
fdprintf(stdout, "0x%08x\n", i);
@ -836,6 +839,7 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
"execute",
"exists",
"export",
"help",
"false", // not in MPW
"parameters",
"quote",
@ -1055,6 +1059,168 @@ int builtin_quit(Environment &, const std::vector<std::string> &tokens, const fd
}
namespace {
template<class Iter>
Iter find_entry(Iter begin, Iter end) {
for(;;) {
begin = std::find(begin, end, '\n');
if (begin == end) return end;
if (std::distance(begin, end) < 3) return end;
++begin;
if (begin[0] == '-' && begin[1] == '\n') return begin;
}
}
template<class Iter1, class Iter2>
bool help_name_match(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) {
if (std::distance(begin2, end2) <= std::distance(begin1, end1)) return false;
for( ; begin1 != end1; ++begin1, ++begin2) {
if (tolower(*begin1) != tolower(*begin2)) return false;
}
if (isspace(*begin2)) return true;
return false;
}
}
bool help_helper(const mapped_file &f, const fdmask &fds, const std::string &cmd) {
/*
* format is:
* -\n
* name whitespace
* ....
* -\n
*/
if (f.size() < 2) return false;
auto iter = f.begin();
auto end = f.end();
if (iter[0] != '-' && iter[1] != '\n') {
iter = find_entry(iter, end);
}
if (cmd.empty()) {
// print first entry
write(stdout, f.begin(), std::distance(f.begin(), iter));
fdputs("\n", stdout);
return true;
}
for(;;) {
if (iter == end) return false;
iter += 2;
auto next = find_entry(iter, end);
auto l = std::distance(iter, end);
if (help_name_match(cmd.begin(), cmd.end(), iter, next)) {
write(stdout, iter, std::distance(iter, next));
fdputs("\n", stdout);
return true;
}
iter = next;
}
}
int builtin_help(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
bool error = false;
filesystem::path _f;
// todo -- -f to specify help file.
auto argv = getopt(tokens, [&](char c){
switch(tolower(c))
{
default:
fdprintf(stderr, "### Help - \"-%c\" is not an option.\n", c);
error = true;
break;
}
});
if (error) {
fdputs("# Usage - Help [-f helpfile] command...\n", stderr);
return 1;
}
const filesystem::path sd(ToolBox::MacToUnix(env.get("shelldirectory")));
const filesystem::path hd = sd / "Help";
filesystem::path mono;
mapped_file mono_file;
std::error_code ec;
if (_f.empty()) {
mono = sd / "MPW.Help";
mono_file = mapped_file(mono, mapped_file::priv, ec);
} else {
mono = _f;
mono_file = mapped_file(mono, mapped_file::priv, ec);
if (!mono_file && !_f.is_absolute()) {
mono = sd / _f;
mono_file = mapped_file(mono, mapped_file::priv, ec);
}
if (!mono_file) {
fdprintf(stderr, "### Help: Unable to open %s\n", _f.c_str());
fdprintf(stderr, "# %s\n", ec.message().c_str());
return 3;
}
}
if (mono_file) {
std::replace(mono_file.begin(), mono_file.end(), '\r', '\n');
}
if (argv.empty()) {
help_helper(mono_file, fds, "");
return 0;
}
int rv = 0;
for (const auto &cmd : argv) {
// 1. check for $MPW:Help:command
filesystem::path p(hd);
p /= cmd;
mapped_file f(p, mapped_file::priv, ec);
if (!ec) {
std::replace(f.begin(), f.end(), '\r', '\n');
write(stdout, f.data(), f.size());
fdputs("\n", stdout);
continue;
}
if (mono_file) {
bool ok = help_helper(mono_file, fds, cmd);
if (ok) break;
}
fdprintf(stderr, "### Help - \"%s\" was not found.\n", cmd.c_str());
rv = 2;
}
return rv;
}
int builtin_execute(Environment &e, const std::vector<std::string> &tokens, const fdmask &fds) {
// runs argv[1] in the current environment. unlike MPW, argv[1] must be a script.

View File

@ -14,6 +14,7 @@ int builtin_directory(Environment &e, const std::vector<std::string> &, const fd
int builtin_echo(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_exists(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_export(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_help(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_parameters(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_quote(Environment &e, const std::vector<std::string> &tokens, const fdmask &);
int builtin_set(Environment &e, const std::vector<std::string> &, const fdmask &);

View File

@ -26,6 +26,12 @@
#include <signal.h>
#include <atomic>
#if defined(__APPLE__)
#include <sys/xattr.h>
#endif
extern std::atomic<int> control_c;
namespace fs = filesystem;
@ -95,7 +101,7 @@ namespace {
case IF:
tokens.pop_back();
try {
e = evaluate_expression("If", std::move(tokens));
e = evaluate_expression(env, "If", std::move(tokens));
}
catch (std::exception &ex) {
fprintf(stderr, "%s\n", ex.what());
@ -107,6 +113,23 @@ namespace {
}
bool is_script(const fs::path &path) {
#if defined(__APPLE__)
/* check for a file type of TEXT */
uint8_t finfo[32];
int ok = getxattr(path.c_str(), XATTR_FINDERINFO_NAME,finfo, sizeof(finfo), 0, 0);
if (ok < 4) return false;
if (memcmp(finfo, "TEXT", 4) == 0) return true;
return false;
#else
return path.extension() == ".script";
#endif
}
}
@ -212,6 +235,7 @@ namespace {
{"execute", builtin_execute},
{"exists", builtin_exists},
{"export", builtin_export},
{"help", builtin_help},
{"parameters", builtin_parameters},
{"quit", builtin_quit},
{"quote", builtin_quote},
@ -358,8 +382,6 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
return status;
}
if (env.test()) return 0;
if (env.startup()) {
fprintf(stderr, "### MPW Shell - startup file may not contain external commands.\n");
return 0;
@ -373,6 +395,23 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
//fprintf(stderr, "### MPW Shell - Command \"%s\" was not found.\n", name.c_str());
//return -1;
}
if (is_script(path)) {
// scripts run with an isolated environment.
Environment new_env = env.subshell_environment();
new_env.set("command", path);
new_env.set_argv(p.arguments);
try {
return read_file(new_env, path, newfds);
} catch (const exit_command_t &ex) {
return ex.value;
}
}
if (env.test()) return 0;
env.set("command", path);
p.arguments[0] = path;
@ -677,7 +716,7 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
if (control_c) throw execution_of_input_terminated();
try {
env.indent_and([&]{
env.loop_indent_and([&]{
rv = vector_command::execute(env, newfds);
});
}

View File

@ -278,7 +278,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
if (is_open()) close();
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
return set_or_throw_error(nullptr, "open");
}

View File

@ -23,6 +23,10 @@ namespace {
bool tf(long v) { return v; }
bool tf(const EnvironmentEntry &e) {
return tf(static_cast<std::string>(e));
}
// used for #. base 10 only, extra chars ignored.
int to_pound_int(const std::string &s) {
if (s.empty()) return 0;
@ -38,6 +42,29 @@ namespace {
}
Environment Environment::subshell_environment() {
/* clone the current environment, do not include local variables */
Environment env;
env._alias_table = _alias_table;
auto &table = env._table;
for (const auto &kv : _table) {
const auto &k = kv.first;
const auto &value = kv.second;
if (!value) continue;
if (k == "echo") env._echo = tf(value);
if (k == "exit") env._exit = tf(value);
if (k == "test") env._test = tf(value);
table.emplace_hint(table.end(), k, value);
}
return env;
}
std::string Environment::get(const std::string & key) const {
auto iter = find(key);
if (iter == end()) return "";
@ -85,15 +112,17 @@ namespace {
set_common(k, std::to_string(value), exported);
}
#if 0
void Environment::set_argv(const std::string &argv0, const std::vector<std::string>& argv) {
set_common("0", argv0, false);
set_argv(argv);
}
#endif
void Environment::set_argv(const std::vector<std::string>& argv) {
_pound = argv.size();
set_common("#", std::to_string(argv.size()), false);
_pound = argv.size() - 1;
set_common("#", std::to_string(_pound), false);
int n = 1;
int n = 0;
for (const auto &s : argv) {
set_common(std::to_string(n++), s, false);
}
@ -116,8 +145,22 @@ namespace {
}
p.pop_back();
set_common("parameters", p, false);
}
void Environment::shift(int n) {
if (n < 0) return;
if (_pound < 1) return;
std::vector<std::string> argv;
argv.push_back(get("0"));
for (int i = 1 + n; i <= _pound; ++i) {
argv.push_back(get(std::to_string(i)));
}
for (int i = 0; i < n; ++i) {
unset(std::to_string(_pound - i));
}
set_argv(argv);
}
@ -185,8 +228,8 @@ namespace {
}
va_list ap;
va_start(ap, fmt);
va_end(ap);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
}

View File

@ -56,11 +56,14 @@ public:
typedef std::vector<std::pair<std::string, std::string>> alias_table_type;
typedef alias_table_type::const_iterator const_alias_iterator;
//const EnvironmentEntry & lookup(const std::string &s);
void set_argv(const std::string &argv0, const std::vector<std::string>& argv);
Environment subshell_environment();
// void set_argv(const std::string &argv0, const std::vector<std::string>& argv);
void set_argv(const std::vector<std::string>& argv);
void shift(int n);
void set(const std::string &k, const std::string &value, bool exported = false);
void set(const std::string &k, long l, bool exported = false);
void unset(const std::string &k);
@ -80,10 +83,6 @@ public:
bool startup() const noexcept { return _startup; }
void startup(bool tf) noexcept { _startup = tf; }
bool passthrough() const noexcept { return _passthrough; }
void passthrough(bool tf) noexcept { _passthrough = tf; }
template<class FX>
void foreach(FX && fx) { for (const auto &kv : _table) { fx(kv.first, kv.second); }}
@ -133,8 +132,6 @@ public:
private:
// magic variables.
friend class indent_helper;
int _indent = 0;
int _loop = 0;
@ -145,7 +142,6 @@ private:
int _status = 0;
int _pound = 0;
bool _startup = false;
bool _passthrough = false;
void set_common(const std::string &, const std::string &, bool);
void rebuild_aliases();
@ -155,16 +151,5 @@ private:
alias_table_type _alias_table;
};
/*
class indent_helper {
public:
indent_helper(Environment &e) : env(e) { env._indent++; }
void release() { if (active) { active = false; env._indent--; }}
~indent_helper() { if (active) env._indent--; }
private:
Environment &env;
bool active = true;
};
*/
#endif

24
error.h
View File

@ -67,6 +67,28 @@ public:
{}
};
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
@ -78,4 +100,4 @@ struct continue_command_t {};
struct exit_command_t { int value = 0; };
struct quit_command_t {};
#endif
#endif

34
lemon_base.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __lemon_base_h__
#define __lemon_base_h__
#include <cstdio>
template<class TokenType>
class lemon_base {
public:
typedef TokenType token_type;
virtual ~lemon_base() = default;
//virtual typename std::enable_if<std::is_move_constructible<TokenType>::value, void>::type
virtual void parse(int yymajor, TokenType &&yyminor) = 0;
virtual void trace(FILE *, const char *) {}
virtual bool will_accept() const = 0;
virtual void reset() {}
protected:
virtual void parse_accept() {}
virtual void parse_failure() {}
virtual void stack_overflow() {}
virtual void syntax_error(int yymajor, TokenType &yyminor) {}
lemon_base() {}
private:
lemon_base(const lemon_base &) = delete;
lemon_base(lemon_base &&) = delete;
lemon_base &operator=(const lemon_base &) = delete;
lemon_base &operator=(lemon_base &&) = delete;
};
#endif

View File

@ -28,4 +28,5 @@ ok = system(*%w(cmake --build build))
ok = system(*%w(git add version.h))
ok = system(*%w(git commit -m), "Bump Version: #{VERSION}")
ok = system(*%w(git tag), "r#{VERSION}")
exit 0
exit 0

269
mpw-regex.cpp Normal file
View 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
View 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

View File

@ -80,12 +80,18 @@
escape = 0xb6;
char = any - escape - ['"{`];
char = any - escape - ['"{`/\\];
escape_seq = escape any;
schar = [^'];
sstring = ['] schar** ['];
fchar = [^/];
fstring = [/] fchar** [/];
bchar = [^\\];
bstring = [\\] bchar** [\\];
vchar = [^}] $vpush;
vchar1 = [^{}] $vpush;
@ -114,6 +120,8 @@
main := (
escape_seq $push
| sstring $push
| fstring $push
| bstring $push
| dstring $push
| vstring
| estring
@ -157,6 +165,14 @@ std::string subshell(const std::string &s, Environment &env, const fdmask &fds)
}
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;

View File

@ -2,6 +2,7 @@
#include "fdset.h"
#include "value.h"
#include "error.h"
#include "mpw-regex.h"
#include <unistd.h>
#include <fcntl.h>
@ -172,8 +173,8 @@ class expression_parser {
public:
expression_parser(const std::string &n, std::vector<token> &&t) :
name(n), tokens(std::move(t))
expression_parser(Environment &e, const std::string &n, std::vector<token> &&t) :
environment(e), name(n), tokens(std::move(t))
{}
expression_parser(const expression_parser &) = delete;
@ -194,6 +195,7 @@ private:
value eval(int op, value &lhs, value &rhs);
value eval_regex(value &lhs, value &rhs);
[[noreturn]] void expect_binary_operator();
[[noreturn]] void end_of_expression();
@ -207,6 +209,7 @@ private:
if (!tokens.empty()) tokens.pop_back();
}
Environment &environment;
const std::string &name;
std::vector<token> tokens;
};
@ -319,12 +322,14 @@ int expression_parser::precedence(int op) {
case '<=':
case '>':
case '>=':
case 0xb2:
case 0xb3:
return 6;
case '==':
case '!=':
case token::equivalent:
case token::not_equivalent:
case '=~':
case '!~':
return 7;
case '&':
return 8;
@ -341,6 +346,25 @@ int expression_parser::precedence(int op) {
//throw std::runtime_error("unimplemented op";);
}
value expression_parser::eval_regex(value &lhs, value &rhs) {
try {
mpw_regex re(rhs.string, true);
// todo -- need environment to store matches.
bool ok = re.match(lhs.string, environment);
return ok ? 1 : 0;
} catch (std::exception &ex) {
std::string error;
error = name;
if (rhs.string.empty() || rhs.string.front() != '/')
error += " - Missing /s around regular expression: ";
else
error += " - Invalid regular expression encountered: ";
error += rhs.string;
throw mpw_error(-5, error);
}
}
value expression_parser::eval(int op, value &lhs, value &rhs) {
switch (op) {
@ -366,9 +390,11 @@ value expression_parser::eval(int op, value &lhs, value &rhs) {
return lhs.to_number() < rhs.to_number();
case '<=':
case 0xb2:
return lhs.to_number() <= rhs.to_number();
case '>=':
case 0xb3:
return lhs.to_number() >= rhs.to_number();
case '>>':
@ -407,6 +433,12 @@ value expression_parser::eval(int op, value &lhs, value &rhs) {
return lhs.string != rhs.string;
case '=~':
return eval_regex(lhs, rhs);
case '!~':
return !eval_regex(lhs, rhs).number;
}
// todo...
throw std::runtime_error("unimplemented op");
@ -469,8 +501,8 @@ int32_t expression_parser::evaluate() {
return v.to_number(1);
}
int32_t evaluate_expression(const std::string &name, std::vector<token> &&tokens) {
int32_t evaluate_expression(Environment &env, const std::string &name, std::vector<token> &&tokens) {
expression_parser p(name, std::move(tokens));
expression_parser p(env, name, std::move(tokens));
return p.evaluate();
}

View File

@ -37,13 +37,22 @@
dchar = escape_seq | (any - escape - ["]);
dstring = ["] dchar** ["] $err{ throw dstring_error(); } ;
# search-forward string
# fschar = escape_seq | (any - escape - [/]);
fchar = [^/];
fstring = [/] fchar** [/] $err{ throw fsstring_error(); } ;
# search-backward string
# bschar = escape_seq | (any - escape - [\\]);
bchar = [^\\];
bstring = [\\] bchar** [\\] $err{ throw bsstring_error(); } ;
action eval { eval }
# > == start state (single char tokens or common prefix)
# % == final state (multi char tokens w/ unique prefix)
# $ == all states
char = any - ['"];
char = any - ['"/\\];
main := |*
ws+ >push_token;
'>>' %push_token => { tokens.emplace_back(">>", '>>'); };
@ -61,6 +70,22 @@
# eval-only.
# macroman ≤
0xb2 when eval
%push_token => { tokens.emplace_back("\xb2", '<='); };
# macroman ≠
0xad when eval
%push_token => { tokens.emplace_back("\xad", '!='); };
# macroman ¬
0xc2 when eval
%push_token => { tokens.emplace_back("\xc2", '!'); };
# macroman ÷
0xd6 when eval
%push_token => { tokens.emplace_back("\xd6", '/'); };
'||' when eval
%push_token => { tokens.emplace_back("||", '||'); };
'|' when eval
@ -69,13 +94,14 @@
'&&' when eval
%push_token => { tokens.emplace_back("&&", '&&'); };
'(' when eval
%push_token => { tokens.emplace_back("(", '('); };
')' when eval
%push_token => { tokens.emplace_back(")", ')'); };
'<>' when eval
%push_token => { tokens.emplace_back("<>", '!='); };
'<<' when eval
%push_token => { tokens.emplace_back("<<", '<<'); };
@ -127,9 +153,16 @@
'-=' when eval
%push_token => { tokens.emplace_back("-=", '-='); };
'=~' when eval
%push_token => { tokens.emplace_back("=~", '=~'); };
'!~' when eval
%push_token => { tokens.emplace_back("!~", '!~'); };
sstring => push_string;
dstring => push_string;
fstring => push_string;
bstring => push_string;
escape_seq => push_string;
char => push;
@ -188,11 +221,19 @@ void unquote(token &t) {
action push { scratch.push_back(fc); }
escape = 0xb6;
char = any - escape - ['"];
char = any - escape - ['"/\\];
schar = [^'] $push;
sstring = ['] schar** ['];
# // and \\ strings retain the delimiter.
fchar = [^/];
fstring = ([/] fchar** [/]) $push;
bchar = [^\\];
bstring = ([\\] bchar** [\\]) $push;
ecode =
'f' ${ scratch.push_back('\f'); }
| 'n' ${ scratch.push_back('\n'); }
@ -208,6 +249,8 @@ void unquote(token &t) {
main := (
escape_seq
| sstring
| fstring
| bstring
| dstring
| char $push
)**;

View File

@ -41,32 +41,50 @@ namespace fs = filesystem;
bool utf8 = false;
fs::path home() {
const char *cp = getenv("HOME");
if (cp && cp) {
auto pw = getpwuid(getuid());
if (pw) return fs::path(pw->pw_dir);
}
return fs::path();
}
fs::path root() {
static fs::path root;
bool init = false;
if (root.empty()) {
const char *cp = getenv("HOME");
if (!cp || !*cp) {
auto pw = getpwuid(getuid());
if (!pw) {
fprintf(stderr,"### Unable to determine home directory\n.");
exit(EX_NOUSER);
static std::array<filesystem::path, 2> locations = { {
"/usr/share/mpw/",
"/usr/local/share/mpw/"
} };
if (!init) {
init = true;
std::error_code ec;
fs::path p;
p = home();
if (!p.empty()) {
p /= "mpw/";
if (fs::is_directory(p, ec)) {
root = std::move(p);
return root;
}
cp = pw->pw_dir;
}
root = cp;
root /= "mpw/";
fs::error_code ec;
fs::file_status st = status(root, ec);
if (!fs::exists(st)) {
fprintf(stderr, "### Warning: %s does not exist.\n", root.c_str());
}
else if (!fs::is_directory(st)) {
fprintf(stderr, "### Warning: %s is not a directory.\n", root.c_str());
for (fs::path p : locations) {
p /= "mpw/";
if (fs::is_directory(p, ec)) {
root = std::move(p);
return root;
}
}
fprintf(stderr, "### Warning: Unable to find mpw directory.\n");
}
return root;
}

View File

@ -64,7 +64,7 @@ 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);

View File

@ -155,7 +155,7 @@ namespace ToolBox
}%%
return str;
return std::move(str);
}

View File

@ -25,6 +25,13 @@ enum {
st_estring2_esc,
st_estring3,
st_fstring,
st_fstring_esc,
st_bstring,
st_bstring_esc,
};
@ -54,6 +61,8 @@ int phase1::process(unsigned char c, int st) {
case st_sstring_esc:
case st_estring1_esc:
case st_estring2_esc:
case st_fstring_esc:
case st_bstring_esc:
multiline = true;
scratch.pop_back();
line++;
@ -81,6 +90,10 @@ text:
return st_sstring;
case '`':
return st_estring;
case '/':
return st_fstring;
case '\\':
return st_bstring;
default:
return st_text;
@ -109,6 +122,23 @@ text:
return st_sstring;
break;
case st_fstring_esc:
// fall through
case st_fstring:
if (c == '/') return st_text;
if (c == esc) return st_fstring_esc;
return st_fstring;
break;
case st_bstring_esc:
// fall through
case st_bstring:
if (c == '\\') return st_text;
if (c == esc) return st_bstring_esc;
return st_bstring;
break;
case st_dstring:
if (c == '\"') return st_text;
if (c == esc) return st_dstring_esc;

View File

@ -60,6 +60,7 @@
}
}
action parse_lparen {
if (scratch.empty()) {
parse(LPAREN, "(");
@ -71,7 +72,8 @@
action parse_rparen {
if (pcount <= 0) {
flush();
parse(RPAREN, ")");
// parse(RPAREN, ")");
scratch.push_back(fc);
fgoto main;
}
--pcount;
@ -86,6 +88,13 @@
schar = [^'];
sstring = ['] schar** ['] ;
fchar = [^/];
fstring = [/] fchar** [/] ;
bchar = [^\\];
bstring = [\\] bchar** [\\] ;
vchar = [^}];
vstring = [{] vchar** [}] ;
@ -112,6 +121,8 @@
| '&' '&' $parse_amp_amp
| escape_seq
| sstring
| fstring
| bstring
| dstring
| vstring
| estring
@ -170,6 +181,10 @@ int phase2::classify() {
%%write data;
if (type) return type;
if (scratch.front() == ')') {
type = RPAREN;
return type;
}
std::string argv0;
const unsigned char *p = (const unsigned char *)scratch.data();

1862
phase3.cpp Normal file

File diff suppressed because it is too large Load Diff

20
phase3.h Normal file
View 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

822
phase3.out Normal file
View 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

View File

@ -1,5 +1,5 @@
#ifndef __version_h__
#define __version_h__
#define VERSION "0.3"
#define VERSION_DATE "Sun Aug 7 15:38:02 2016"
#define VERSION "0.4"
#define VERSION_DATE "Sat Sep 24 12:51:22 2016"
#endif