[Commit] nickle ChangeLog, 1.129, 1.130 builtin.5c, 1.9, 1.10 command.5c, 1.20, 1.21 compile.c, 1.163, 1.164 parse-args.5c, 1.2, 1.3

Bart Massey commit at keithp.com
Mon Aug 1 02:20:32 PDT 2005


Committed by: bart

Update of /local/src/CVS/nickle
In directory home.keithp.com:/tmp/cvs-serv26728

Modified Files:
	ChangeLog builtin.5c command.5c compile.c parse-args.5c 
Log Message:
2005-07-31  Bart Massey  <bart at cs.pdx.edu>

	* parse-args.5c:
	Big rewrite of parse-args code to work better and be
	more usable. Will now accomodate Nickle startup.
	
	* builtin.5c, command.5c:
	Changes to use parse-args instead of ad hoc argument parsing
	at startup.  Changed key conventions: -e now takes a single
	argument; -e, -f, and -l can be freely mixed; interactive mode
	can now have argv through "--"; others?

	* compile.c:
	Fixed trivial spelling error.



Index: ChangeLog
===================================================================
RCS file: /local/src/CVS/nickle/ChangeLog,v
retrieving revision 1.129
retrieving revision 1.130
diff -u -d -r1.129 -r1.130
--- ChangeLog	9 Jul 2005 05:46:53 -0000	1.129
+++ ChangeLog	1 Aug 2005 09:20:29 -0000	1.130
@@ -1,3 +1,18 @@
+2005-07-31  Bart Massey  <bart at cs.pdx.edu>
+
+	* parse-args.5c:
+	Big rewrite of parse-args code to work better and be
+	more usable. Will now accomodate Nickle startup.
+	
+	* builtin.5c, command.5c:
+	Changes to use parse-args instead of ad hoc argument parsing
+	at startup.  Changed key conventions: -e now takes a single
+	argument; -e, -f, and -l can be freely mixed; interactive mode
+	can now have argv through "--"; others?
+
+	* compile.c:
+	Fixed trivial spelling error.
+	
 2005-07-08  Keith Packard  <keithp at keithp.com>
 
 	* builtin-sockets.c: (do_Socket_connect):

Index: builtin.5c
===================================================================
RCS file: /local/src/CVS/nickle/builtin.5c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- builtin.5c	30 Nov 2004 18:34:41 -0000	1.9
+++ builtin.5c	1 Aug 2005 09:20:29 -0000	1.10
@@ -111,6 +111,7 @@
 library "history.5c";
 
 # Now load the extra commands.
+library "parse-args.5c";
 library "command.5c";
 
 # Note that some libraries extend namespaces, and
@@ -127,5 +128,5 @@
 
 # parse the command line
 extend namespace Command {
-      argv = process_args (argv);
+      process_args (&argv);
 }

Index: command.5c
===================================================================
RCS file: /local/src/CVS/nickle/command.5c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- command.5c	8 Jun 2004 09:30:53 -0000	1.20
+++ command.5c	1 Aug 2005 09:20:29 -0000	1.21
@@ -1,4 +1,5 @@
 extend namespace Command {
+
     /*
      * These are used by the parser when building
      * programs to run from the top level of the
@@ -226,78 +227,105 @@
     
     new ("char", do_char);
 
-    /*
-     * Command line processing
-     */
-    string[*] process_args(string[*] argv) 
+    void process_args(&string[*] argv)
     {
-	typedef struct {
-	    bool	    library;
-	    string	    name;
-	} lex_arg;
-	lex_arg[dim(argv)]	files;
-	int			nfiles = 0;
-	bool		lex_stdin = true;
-	bool		done = false;
-	int			i = 0;
+	bool lex_stdin = true;
+	string script_name = "";
 
-	while (!done && i < dim (argv))
-	{
-	    switch (argv[i]) {
-	    case "-e":
-		/*
-		 * -e -- remaining arguments are a single expression
-		 */
-		string  expr = "";
-		while (++i < dim(argv))
-		{
-		    expr += argv[i];
-		    if (i < dim(argv)-1)
-			expr += " ";
-		    else
-			expr += "\n";
-		}
-		lex_string (expr);
-		done = true;
-		lex_stdin = false;
-		break;
-	    case "-f":
-		/*
-		 * -f -- lex a file
-		 */
-		i++;
-		files[nfiles].library = false;
-		files[nfiles].name = argv[i];
-		nfiles++;
-		i++;
-		break;
-	    case "-l":
-		i++;
-		files[nfiles].library = true;
-		files[nfiles].name = argv[i];
-		nfiles++;
-		i++;
-		break;
-	    default:
-		/*
-		 * Add the script base directory to the library path
-		 */
-		nickle_path += ":" + String::dirname(argv[0]);
-		files[nfiles].library = false;
-		files[nfiles].name = argv[i];
-		nfiles++;
-		done = true;
-		lex_stdin = false;
-		break;
-	    }
+	/* XXX Much of this architecture is driven by
+	   the fact that the lexer stacks the
+	   files to be processed rather than queueing them. */
+
+	typedef union {
+	    string library;
+	    string sfile;
+	    string script;
+	    string expr;
+	} lexable;
+
+	lexable[...] lexables = {};
+
+	void process_lexables() {
+	}
+
+	void save_lexable(lexable l) {
+	    lexables[dim(lexables)] = l;
+	}
+
+	void save_library(string arg) {
+	    save_lexable((lexable.library)arg);
+	}
+	
+	void save_script(string arg) {
+	    save_lexable((lexable.script)arg);
+	    script_name = arg;
+	    lex_stdin = false;
+	}
+	
+	void save_file(string arg) {
+	    save_lexable((lexable.sfile)arg);
+	}
+	
+	void save_expr(string arg) {
+	    save_lexable((lexable.expr)arg);
+	    lex_stdin = false;
+	}
+	
+	ParseArgs::argdesc argd = {
+	    args = {
+		{var = {arg_lambda = save_library},
+		 abbr = 'l',
+		 name = "library",
+		 expr_name = "lib",
+		 desc = "Library to load."},
+		{var = {arg_lambda = save_file},
+		 abbr = 'f',
+		 name = "file",
+		 expr_name = "source",
+		 desc = "Source file to load."},
+		{var = {arg_lambda = save_expr},
+		 abbr = 'e',
+		 name = "expr",
+		 expr_name = "expression",
+		 desc = "Expression to evaluate."}
+	    },
+	    posn_args = {
+		{var = {arg_lambda = save_script},
+		 name = "script",
+		 optional = <>}
+	    },
+	    unknown = &(int user_argind),
+	    prog_name = "nickle"
+	};
+
+	/* actually parse the arguments */
+	ParseArgs::parseargs(&argd, &argv);
+
+	/* Reset argv to hold remaining arguments */
+	if (is_uninit(&user_argind)) {
+	    string[0] rest = {};
+	    argv = rest;
+	} else {
+	    string[dim(argv) - user_argind + 1] rest;
+	    rest[0] = script_name;
+	    for (int i = 1; i < dim(rest); i++)
+		rest[i] = argv[i + user_argind - 1];
+	    argv = rest;
 	}
+
+	/* Recall the comment above.  Since the lexer
+	   stacks, we must stack stdin, which is to run last,
+	   *before* the other stuff.  Bleah. */
 	if (lex_stdin)
 	{
-	    /*
-	     * Add the current directory to the library path
-	     */
+	    /* Add the current directory to the library path */
 	    nickle_path += ":.";
+
+	    /* we want stdin to be processed after all other lexables */
 	    lex_input (stdin, "<stdin>", false, File::isatty (stdin));
+
+	    /* if there's a .nicklerc, we apparently want that
+	       next-to-last? */
 	    try {
 		lex_file (Environ::get ("HOME") + "/.nicklerc");
 	    } catch invalid_argument (msg, int i, poly value) {
@@ -309,31 +337,39 @@
 	    }
 	}
 
-	while (nfiles-- > 0)
-	{
-	    exception load_fail (string msg);
-	    try {
-		if (files[nfiles].library) {
-		    if (!lex_library (files[nfiles].name))
-			raise load_fail ("no such library");
-		} else {
-		    try {
-			lex_file (files[nfiles].name);
-		    } catch File::open_error (string msg,
-					      File::error_type err,
-					      string name) {
-			raise load_fail (msg);
-		    }
-		}
-	    } catch load_fail (string msg) {
+	/* now stack the other arguments, in reverse order */
+	for (int i = dim(lexables) - 1; i >=0; --i) {
+	    static void load_fail(string name, string msg) {
 		File::fprintf (stderr, "Cannot load \"%s\": %s\n",
-			       files[nfiles].name, msg);
+			       name, msg);
 		exit (1);
 	    }
+
+	    static void lex_script(string arg) {
+		try {
+		    lex_file (arg);
+		} catch File::open_error (string msg,
+					  File::error_type err,
+					  string name) {
+		    load_fail (name, msg);
+		}
+	    }
+
+	    union switch(lexables[i]) {
+	    case library arg:
+		if (!lex_library (arg))
+		    load_fail (arg, "cannot load library");
+		break;
+	    case script arg:
+		lex_script(arg);
+		break;
+	    case sfile arg:
+		lex_script(arg);
+		break;
+	    case expr arg:
+		lex_string(arg + "\n");
+		break;
+	    }
 	}
-	/*
-	 * Reset argv to hold remaining arguments
-	 */
-	return (string[dim(argv) - i]) { [j] = argv[i+j] };
     }
 }

Index: compile.c
===================================================================
RCS file: /local/src/CVS/nickle/compile.c,v
retrieving revision 1.163
retrieving revision 1.164
diff -u -d -r1.163 -r1.164
--- compile.c	9 Jul 2005 05:46:54 -0000	1.163
+++ compile.c	1 Aug 2005 09:20:29 -0000	1.164
@@ -3542,7 +3542,7 @@
 			CompileIsReachable (obj, obj->used))
 		    {
 			CompileError (obj, expr, 
-				      "Fall through case with varient value");
+				      "Fall-through case with variant value");
 		    }
 		    inst->tagcase.offset = obj->used - case_inst[icase];
 		    inst->tagcase.tag = tag;

Index: parse-args.5c
===================================================================
RCS file: /local/src/CVS/nickle/parse-args.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- parse-args.5c	8 Jun 2004 09:30:54 -0000	1.2
+++ parse-args.5c	1 Aug 2005 09:20:30 -0000	1.3
@@ -1,14 +1,13 @@
-import File;
-import String;
-
 namespace ParseArgs {
+    import File;
+    import String;
 
     public typedef union {
 	&bool arg_flag;
 	&string arg_string;
 	&int arg_int;
 	&real arg_real;
-	&(string[*]) arg_list;
+	(void(string)) arg_lambda;
     } arg_var;
 
     public typedef struct {
@@ -20,13 +19,24 @@
     } arg;
 
     public typedef struct {
+	arg_var var;
+	void optional;
+	string name;
+    } posn_arg;
+
+    public typedef struct {
 	string prog_name;
 	arg[*] args;
-	&(string[*]) rest;
+	posn_arg[*] posn_args;
 	&int unknown;
     } argdesc;
 
-    string arg_string(arg a) {
+    string arg_string(arg a)
+	/*
+	 * Given an arg desc, produce
+	 * a printable representation
+	 */
+    {
 	string stdarg() {
 	    if (is_uninit(&a.name))
 		return a.expr_name;
@@ -37,17 +47,12 @@
 	    return s;
 	}
 	union switch (a.var) {
-	case arg_list:
-	    return sprintf("%s ...", a.expr_name);
 	case arg_flag:
 	    return stdarg();
 	default:
 	    return sprintf("%s <%s>", stdarg(), a.expr_name);
 	}
-	abort("internal error");
-    }
-
-    void print_options(&(arg[*]) a) {
+	abort("internal error: unknown arg type");
     }
 
     public string basename(string path) 
@@ -61,13 +66,37 @@
 	return substr(path, p + 1, length(path) - p - 1);
     }
 
+    /*
+     * the argument description was defective, with
+     * a reason string
+     */
+    public exception bad_argd(string reason);
+
     public void parseargs(&argdesc argd, &(string[*]) argv) 
 	/*
 	 * Parse arguments in 'argv' according to 'argd'
 	 */
     {
-	argd.prog_name = basename(argv[0]);
+	/*
+	 * validate that any argd.posn_args
+	 * consists of mandatory followed by optional args
+	 */
+	if (!is_uninit(&argd.posn_args))
+	    for (int i = 1; i < dim(argd.posn_args); i++)
+		if (!is_uninit(&argd.posn_args[i - 1].optional) &&
+		    is_uninit(&argd.posn_args[i].optional))
+		    raise bad_argd("fixed after optional posn arg");
+
+	/* argument that is current focus */
+	int argind = 0;
+
+	/* save program basename as needed */
+	if (is_uninit(&argd.prog_name)) {
+	    argd.prog_name = basename(argv[0]);
+	    argind++;
+	}
 
+	/* build hashes for flags */
 	arg[int] arg_abbr_hash = {};
 	arg[string] arg_name_hash = {};
 	for (int i = 0; i < dim(argd.args); i++) {
@@ -78,30 +107,46 @@
 		arg_name_hash[d.name] = d;
 	}
 
-	void usage() {
-	    fprintf(stderr, "%s: usage: ", argd.prog_name);
-	    fprintf(stderr, "%s <options>", argd.prog_name);
+	void usage(file f)
+	    /*
+	     * Print a description of program usage on f
+	     */
+	{
+	    fprintf(f, "%s: usage: ", argd.prog_name);
+	    fprintf(f, "%s <options>", argd.prog_name);
 	    for (int i = 0; i < dim(argd.args); i++)
 		if (is_uninit(&argd.args[i].name))
-		    fprintf(stderr, " %s", arg_string(argd.args[i]));
-	    fprintf(stderr, "\n");
+		    fprintf(f, " %s", arg_string(argd.args[i]));
+	    if (!is_uninit(&argd.posn_args)) {
+		for (int i = 0; i < dim(argd.posn_args); i++)
+		    if (is_uninit(&argd.posn_args[i].optional))
+			fprintf(f, " %s", argd.posn_args[i].name);
+		    else
+			fprintf(f, " [%s]", argd.posn_args[i].name);
+	    }
+	    if (!is_uninit(&&argd.unknown))
+		fprintf(f, " [--] ...");
+	    fprintf(f, "\n");
 	    for (int i = 0; i < dim(argd.args); i++)
 		if (is_uninit(&argd.args[i].name))
-		    fprintf(stderr, "    %s   %s\n",
+		    fprintf(f, "    %s   %s\n",
 			    argd.args[i].expr_name,
 			    argd.args[i].desc);
 	    for (int i = 0; i < dim(argd.args); i++)
 		if (!is_uninit(&argd.args[i].name))
-		    fprintf(stderr, "    %s   %s\n",
+		    fprintf(f, "    %s   %s\n",
 			    arg_string(argd.args[i]),
 			    argd.args[i].desc);
 	    exit(1);
 	}
 
-	int argi = 1;
-
-	void save_value(&arg a, string value) {
-	    union switch(a.var) {
+	void save_value(&arg_var a, string value)
+	    /*
+	     * convert and store the value
+	     * associated with an argument
+	     */
+	{
+	    union switch(a) {
 	    case arg_flag var:
 		var = true;
 		break;
@@ -109,95 +154,123 @@
 		var = value;
 		break;
 	    case arg_int var:
-		var = atoi(value);
+		var = string_to_integer(value);
 		break;
 	    case arg_real var:
-		var = atof(value);
+		var = string_to_real(value);
+		break;
+	    case arg_lambda var:
+		var(value);
 		break;
-	    case arg_list:
-		abort("flagged list in argument description");
 	    }
 	}
 
-	void process_flag(&arg a) {
-	    if (argi >= dim(argv)) {
+	void process_flag(&arg a)
+	    /*
+	     * Process a flag that requires an argument
+	     */
+	{
+	    if (argind >= dim(argv)) {
 		fprintf(stderr, "%s: missing value for --%s\n",
 			argd.prog_name, a.name);
-		usage();
+		usage(stderr);
 	    }
-	    save_value(&a, argv[argi]);
-	    argi++;
+	    save_value(&a.var, argv[argind]);
+	    argind++;
 	}
 
-	while(argi < dim(argv)) {
-	    string a = argv[argi];
-	    if (a == "" || a == "-" || a == "--") {
-		if (is_uninit(&argd.unknown)) {
+	/* handle the help case first */
+	if (argind < dim(argv) &&
+	    (argv[argind] == "--help" || argv[argind] == "--usage"))
+	    usage(stdout);
+	
+	/* now walk the arguments */
+	while(argind < dim(argv)) {
+	    string a = argv[argind];
+
+	    /* we're done on a "user stop" */
+	    if (a == "--") {
+		if (is_uninit(&&argd.unknown)) {
 		    fprintf(stderr,
 			    "%s: don't know what to do with arg \"%s\"\n",
 			    argd.prog_name,
 			    a);
-			usage();
+			usage(stderr);
 		}
-		argd.unknown = argi + 1;
-		return;
+		break;
 	    }
+
+	    /* process abbreviated (old-style) flags (maybe grouped) */
 	    if (a[0] == '-' && a[1] != '-') {
-		argi++;
+		argind++;
 		for (int i = 1; i < length(a); i++) {
 		    if (!hash_test(arg_abbr_hash, a[i])) {
 			fprintf(stderr, "%s: unknown flag char '-%c'\n",
 				argd.prog_name,
 				a[i]);
-			usage();
+			usage(stderr);
 		    }
 		    process_flag(&arg_abbr_hash[a[i]]);
 		}
 		continue;
 	    }
+
+	    /* process a long flag */
 	    if (a[0] == '-' && a[1] == '-') {
 		string argname = substr(a, 2, length(a) - 2);
 		if (!hash_test(arg_name_hash, argname)) {
 			fprintf(stderr, "%s: unknown flag \"--%s\"\n",
 				argd.prog_name,
 				argname);
-			usage();
+			usage(stderr);
 		}
-		argi++;
+		argind++;
 		process_flag(&arg_name_hash[argname]);
 		continue;
 	    }
-	    break;
+
+	    /* maybe something will suck up remaining args  */
+	    if (!is_uninit(&argd.posn_args) ||
+		!is_uninit(&&argd.unknown))
+		break;
+
+	    fprintf(stderr, "%s: extra argument \"%s\"\n",
+		    argd.prog_name,
+		    argv[argind]);
+	    usage(stderr);
 	}
-	for (int i = 0; i < dim(argd.args); i++) {
-	    if (!is_uninit(&argd.args[i].name))
-		continue;
-	    if (argi >= dim(argv)) {
-		fprintf(stderr, "%s: missing required argument %s\n",
-			argd.prog_name, argd.args[i].expr_name);
-		usage();
-	    }
-	    union switch (argd.args[i].var) {
-	    case arg_list v:
-		string[...] rest = {};
-		v = rest;
-		while(argi < dim(argv)) {
-		    rest[argi] = argv[argi];
-		    argi++;
+
+	/* process any positional arguments */
+	if (!is_uninit(&argd.posn_args)) {
+	    for (int i = 0; i < dim(argd.posn_args); i++) {
+		if (argind >= dim(argv) ||
+		    argv[argind] == "--") {
+		    if (!is_uninit(&argd.posn_args[i].optional))
+			break;
+		    fprintf(stderr, "%s: missing required argument %s\n",
+			    argd.prog_name,
+			    argd.args[i].expr_name);
+		    usage(stderr);
 		}
-		return;
-	    case arg_flag:
-		abort("fixed flag in argument description");
-	    default:
-		save_value(&argd.args[i], argv[argi]);
-		argi++;
+		save_value(&argd.posn_args[i].var, argv[argind]);
+		argind++;
 	    }
 	}
-	if (argi < dim(argv)) {
-	    fprintf(stderr, "%s: unknown or extra argument \"%s\"\n",
-		    argv[argi],
-		    argd.prog_name);
-	    usage();
+
+	/* process any unknown arguments */
+	if (argind < dim(argv)) {
+	    if (argv[argind] == "--")
+		argind++;
+	    if (!is_uninit(&&argd.unknown)) {
+		argd.unknown = argind;
+		return;
+	    }
+	    if (argind < dim(argv)) {
+	        fprintf(stderr, "%s: unknown or extra argument \"%s\"\n",
+			argd.prog_name,
+			argv[argind]);
+		usage(stderr);
+	    }
 	}
     }
 }




More information about the Commit mailing list