Commit 3e3e2ec2 authored by Samuel Newbold's avatar Samuel Newbold

.local_declare declares but does not define vars

parent fefded1c
// The definition of the Argm class, which contains the arguments that may
// be passed to an executable.
//
// Copyright (C) 2005-2017 Samuel Newbold
// Copyright (C) 2005-2019 Samuel Newbold
#include <cstring>
#include <cstdlib>
......@@ -72,7 +72,8 @@ Argm& Argm::operator=(const Argm& src) {
parent_map_v = src.parent_map();
input = src.input;
output = src.output;
error = src.error;}
error = src.error;
return *this;}
Argm::~Argm(void) {
delete argfunction_v;}
......@@ -102,7 +103,7 @@ std::string Argm::get_var(const std::string& key) const {
case '8': case '9': case '0': {
try {
int n = my_strtoi(key.c_str());
if (argv_v.size() > n) return argv_v[n];
if ((int)argv_v.size() > n) return argv_v[n];
else return std::string();}
catch (E_nan ex) {throw Exception(Argm::Undefined_variable, key);}}
default: return parent_map()->get(key);}}
......@@ -116,7 +117,7 @@ bool Argm::var_exists(const std::string& key) const {
case '1': case '2': case '3': case '4': case '5': case '6':
case '7': case '8': case '9': case '0': {
int n = std::atoi(key.c_str());
return argv_v.size() > n;}
return (int)argv_v.size() > n;}
default: return parent_map()->exists(key, true);}}
void Argm::global(const std::string& key, const std::string& value) const {
......@@ -133,6 +134,14 @@ void Argm::local(const std::string& key, const std::string& value) const {
throw Exception(Argm::Illegal_variable_name, key);
default: parent_map()->local(key, value);}}
void Argm::local_declare(const std::string& key, Error_list& exceptions) const{
switch (key[0]) {
case '#': case '*': case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '0':
exceptions.add_error(Exception(Argm::Illegal_variable_name, key));
break;
default: parent_map()->local_declare(key);}}
void Argm::locals_listed(void) const {
parent_map()->locals_listed = true;}
......
// Copyright (C) 2005-2018 Samuel Newbold
// Copyright (C) 2005-2019 Samuel Newbold
class Command_block;
struct Error_list;
class Argm {
public:
......@@ -121,6 +122,7 @@ class Argm {
std::string get_var(const std::string& key) const;
void global(const std::string& key, const std::string& value) const;
void local(const std::string& key, const std::string& value) const;
void local_declare(const std::string& key, Error_list& exceptions) const;
void locals_listed(void) const;
void set_var(const std::string& key, const std::string& value) const;
template<class Out>
......
// The functions that implement each of the builtin executables
//
// Copyright (C) 2006-2018 Samuel Newbold
// Copyright (C) 2006-2019 Samuel Newbold
#include <algorithm>
#include <climits>
......@@ -481,6 +481,13 @@ void b_local(const Argm& argm, Error_list& exceptions) {
if (argm.argfunction()) throw Exception(Argm::Excess_argfunction);
argm.local(argm[1], argm[2]);}
// add, but don't define a variable until the enclosing function terminates
void b_local_declare(const Argm& argm, Error_list& exceptions) {
if (argm.argc() < 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
if (argm.argfunction()) throw Exception(Argm::Excess_argfunction);
for (unsigned i=1; i<argm.argc(); ++i)
argm.local_declare(argm[i], exceptions);}
// list the files specified by the arguments if they exist
void b_ls(const Argm& argm, Error_list& exceptions) {
if (argm.argc() < 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
......
// Copyright (C) 2006-2017 Samuel Newbold
// Copyright (C) 2006-2019 Samuel Newbold
void b_argc(const Argm& argm, Error_list& exceptions);
void b_binary(const Argm& argm, Error_list& exceptions);
......@@ -40,6 +40,7 @@ void b_list_environment(const Argm& argm, Error_list& exceptions);
void b_list_executables(const Argm& argm, Error_list& exceptions);
void b_list_locals(const Argm& argm, Error_list& exceptions);
void b_local(const Argm& argm, Error_list& exceptions);
void b_local_declare(const Argm& argm, Error_list& exceptions);
void b_ls(const Argm& argm, Error_list& exceptions);
void b_nop(const Argm& argm, Error_list& exceptions);
void b_replace_exception(const Argm& argm, Error_list& exceptions);
......
......@@ -2,7 +2,7 @@
// between the arguments passed to and the parameters received by a function
// or the argument function of .scope
//
// Copyright (C) 2015-2017 Samuel Newbold
// Copyright (C) 2015-2019 Samuel Newbold
#include <set>
#include <string>
#include <vector>
......@@ -284,7 +284,7 @@ void Prototype::unused_var_check(Variable_map* vars, Error_list& errors) const {
vars->used_vars_insert("-*"); // in the absent else case $-* is not defined
vars->used_vars_insert("-?"); // in the absent else case $-? is not defined
for (auto i: positional) i.unused_pos_var_check(vars, errors);
for (auto j: vars->locals()) if (!vars->used_vars_contains(j)) {
if (j == "?"); // $? does not need to be used (in subshells)
else errors.add_error(Exception(Argm::Unused_variable, j));
vars->used_vars_insert(j);}}
for (auto j: vars->locals())
if (!vars->used_vars_contains(j) && !vars->undefined_vars_contains(j)) {
errors.add_error(Exception(Argm::Unused_variable, j));
vars->used_vars_insert(j);}}
// the registering of builtins
// Copyright Samuel Newbold 2005-2018
// Copyright Samuel Newbold 2005-2019
#include <list>
#include <map>
......@@ -89,7 +89,7 @@ void internal_init(Error_list& exceptions) {
executable_map.set(new Builtin(".internal_functions", b_internal_functions));
executable_map.set(new Function(".internal_vars",
empty_prototype.begin(), empty_prototype.end(), false,
"{.echo (FIGNORE ?\n)}", exceptions));
"{.echo (FIGNORE\n)}", exceptions));
executable_map.set(new Builtin(".is_default_input", b_is_default_input));
executable_map.set(new Builtin(".is_default_output", b_is_default_output));
executable_map.set(new Builtin(".is_default_error", b_is_default_error));
......@@ -101,6 +101,7 @@ void internal_init(Error_list& exceptions) {
executable_map.set(new Builtin(".list_executables", b_list_executables));
executable_map.set(new Builtin(".list_locals", b_list_locals));
executable_map.set(new Builtin(".local", b_local));
executable_map.set(new Builtin(".local_declare", b_local_declare));
executable_map.set(new Builtin(".ls", b_ls));
executable_map.set(new Builtin(".nop", b_nop));
executable_map.set(new Builtin(".replace_exception", b_replace_exception));
......
......@@ -106,8 +106,7 @@ fns grep [-l] [regex] [file ...] {
if_only .var_exists file {.selection_set s $file}
.try_catch_recursive .return_code {
.if var_exists -l {
.local result ()
.nop $result
.local_declare result
.store_output result {&{whichp grep} $-*$ $REGEX @$s}
.echo $result
.set grep-sel $result$}
......
This diff is collapsed.
......@@ -904,13 +904,15 @@ se {.fork se {
/bin/kill -INT ${.getpid}
echo should not continue beyond SIGINT}
# .global .local .unset .var_exists
# .global .local .local_declare .unset .var_exists
.global
.global x y z
.global x y {excess argfunc}
.local
.local x y z
.local x y {excess argfunc}
.local_declare
.local_declare x {excess argfunc}
.unset
.unset x {excess argfunc}
.unset x y
......@@ -919,6 +921,7 @@ se {.fork se {
.var_exists x {excess argfunc}
.global 100 nihilism
.local 0 nihilism
.local_declare 0 #
.global .var_exists (must be requested to be checked)
echo $.var_exists$
.unset #
......@@ -941,37 +944,85 @@ echo $.var_exists$
echo $x
.global x nihilism
.global x ubernihilism
.function_all_flags a {if_only .var_exists x {e in a x \( $x \) $nl}
if_only .var_exists y {e in a y \( $y \) $nl}
.local x (first level not global)
.local y (level one not global)
b
if_only .var_exists x {e out a x \( $x \) $nl}
if_only .var_exists y {e out a y \( $y \) $nl}}
.function_all_flags b {if_only .var_exists x {e in b x \( $x \) $nl}
if_only .var_exists y {e in b y \( $y \) $nl}
.local x (second level masks first)
.set y (level two overwrites one)
c
if_only .var_exists x {e out b x \( $x \) $nl}
if_only .var_exists y {e out b y \( $y \) $nl}}
.function_all_flags c {if_only .var_exists x {e in c x \( $x \) $nl}
if_only .var_exists y {e in c y \( $y \) $nl}
# can unset a local, but only one at a time
.unset x
.try_catch_recursive .global_would_be_masked {
.global y (attempting to create global masked by local)}
.set x (third level overwrites first)
.local x (third level masks first)
.set y (level three overwrites one)
if_only .var_exists x {e out c x \( $x \) $nl}
if_only .var_exists y {e out c y \( $y \) $nl}}
.global i nihilism
.global j nihilism
fn a {if_only .var_exists x {echo in a x $x}
if_only .var_exists y {echo in a y $y}
if_only .var_exists i {echo in a i $i}
if_only .var_exists j {echo in a j $j}
if_only .var_exists k {echo in a k $k}
if_only .var_exists m {echo in a m $m}
.local x (first level not global)
.local y (level one not global)
.local z (will be unused)
.local_declare i j k l m
.set i (declared first level not global)
.set j (declared first level not global)
.set k (declared level one not global)
.set l (will be unused)
b
if_only .var_exists i {echo out a i $i}
if_only .var_exists j {echo out a j $j}
if_only .var_exists k {echo out a k $k}
if_only .var_exists m {echo out a m $m}
if_only .var_exists x {echo out a x $x}
if_only .var_exists y {echo out a y $y}}
fn b {if_only .var_exists i {echo in b i $i}
if_only .var_exists j {echo in b j $j}
if_only .var_exists k {echo in b k $k}
if_only .var_exists m {echo in a m $m}
if_only .var_exists x {echo in b x $x}
if_only .var_exists y {echo in b y $y}
.local x (second level masks first)
.set y (level two overwrites one)
.local_declare i j m
echo undefined i will hide the first level local
.set j (declared second level masks first)
.set k (declared level two overwrites one)
c
if_only .var_exists i {echo out b i $i}
if_only .var_exists j {echo out b j $j}
if_only .var_exists k {echo out b k $k}
if_only .var_exists m {echo out b m $m}
if_only .var_exists x {echo out b x $x}
if_only .var_exists y {echo out b y $y}}
fn c {if_only .var_exists i {echo in c i $i}
if_only .var_exists j {echo in c j $j}
if_only .var_exists k {echo in c k $k}
if_only .var_exists m {echo in c m $m}
if_only .var_exists x {echo in c x $x}
if_only .var_exists y {echo in c y $y}
# can unset a local, but only one at a time
.unset x
.try_catch_recursive .undefined_variable {
.unset i}
.unset j
.unset l
.try_catch_recursive .global_would_be_masked {
.global y (attempting to create global masked by local)}
.set x (third level overwrites first)
.set i (declared third level overwrites second)
.set j (declared third level overwrites first)
.local x (third level masks first)
.local_declare i j m
.set i (declared third level masks first)
.set j (declared third level masks first)
.set y (level three overwrites one)
.set k (declared level three overwrites one)
if_only .var_exists i {echo out c i $i}
if_only .var_exists j {echo out c j $j}
if_only .var_exists k {echo out c k $k}
if_only .var_exists m {echo out c m $m}
if_only .var_exists x {echo out c x $x}
if_only .var_exists y {echo out c y $y}}
a
# demonstrating that final values are not retained
a
echo $x
.var_exists y
.unset x
.unset i
.unset j
.var_exists x
# .store_output
......@@ -1532,7 +1583,7 @@ wrapper 1 2
.nop .toggle_readline
.nop .toggle_readline
# .test_executable_exists .type .whence_function
# .test_executable_exists .type .whence_function
# Arg_script::str() but only an unknown fraction of the lines
# Arg_spec::str() (except trailing whitespace) only through SOON case
.test_executable_exists
......@@ -1635,7 +1686,7 @@ whence .mapped_argfunction {>dummy_file}
.usleep -6
.usleep 5i
.usleep 800
# .usleep_overhead
# .usleep_overhead
.execution_count
.execution_count j
.scope () {
......@@ -1823,7 +1874,7 @@ echo ${.version}
.version_compatible 1.0
.version_compatible 0.3+
# internal functions
# internal functions
# .after_command .raw_command .prompt
# all of these are used as part of the test itself, though no longer in a
# significant way.
......
// Functions to implement a variable map, and permit it to be exported as the
// environment for child processes.
//
// Copyright (C) 2006-2018 Samuel Newbold
// Copyright (C) 2006-2019 Samuel Newbold
#include <cstdlib>
#include <cstring>
......@@ -105,6 +105,14 @@ void Variable_map::local(const std::string& key, const std::string& value) {
local_vars.insert(key);
param(key, value);}
// locals have their usage checked directly
void Variable_map::local_declare(const std::string& key) {
if (usage_checked)
throw Exception(Argm::Internal_error,
"variable map added to after usage checked");
undefined_vars.insert(key);
local_vars.insert(key);}
void Variable_map::set(const std::string& key, const std::string& value) {
auto i = find(key);
if (i != end())
......@@ -112,7 +120,9 @@ void Variable_map::set(const std::string& key, const std::string& value) {
used_vars.insert(key); // we're about to throw a more specific error
throw Exception(Argm::Unused_before_set, key);}
else i->second = value;
else if (undefined_vars.find(key) != undefined_vars.end()) param(key, value);
else if (undefined_vars.find(key) != undefined_vars.end()) {
undefined_vars.erase(key);
param(key, value);}
else if (parent) parent->set(key, value);
else throw Exception(Argm::Undeclared_variable, key);}
......@@ -121,6 +131,8 @@ void Variable_map::unset(const std::string& key) {
throw Exception(Argm::Illegal_variable_name, key);
auto i = find(key);
if (i != end()) erase(i);
else if (undefined_vars.find(key) != undefined_vars.end())
throw Exception(Argm::Undefined_variable, key);
else if (parent) parent->unset(key);
else throw Exception(Argm::Undeclared_variable, key);}
......
// Copyright (C) 2006-2018 Samuel Newbold
// Copyright (C) 2006-2019 Samuel Newbold
extern const char* WSPACE;
std::string escape(const std::string& src);
......@@ -44,10 +44,13 @@ public:
void global(const std::string& key, const std::string& value);
void param(const std::string& key, const std::string& value);
void local(const std::string& key, const std::string& value);
void local_declare(const std::string& key);
const std::set<std::string>& locals(void) const {return local_vars;};
void set(const std::string& key, const std::string& value);
bool simple_exists(const std::string& key) const {return find(key) != end();}
void unset(const std::string& key);
bool undefined_vars_contains(const std::string& key) const {
return undefined_vars.find(key) != undefined_vars.end();};
bool used_vars_contains(const std::string& key) const {
return used_vars.find(key) != used_vars.end();};
bool checked_vars_contains(const std::string& key) const {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment