Sample Parser
The parser provides a solid basis for any command evaluation for "termlib.js". It splits the current command line to arguments (words) and takes care of any white space and quotes. Even escape characters and options are supported.
Synopsis:
var parser = new Parser();
parser.parseLine(this);
var command = this.argv[this.argc++];
Usage:
Call method "parseLine(this)" from inside of your Terminal-handler.
This will parse the current command line to chunks separated by any amount of
white space (see property whiteSpace).
"parseLine" will inject the following properties into the Terminal instance:
this.argv: |
Array of parsed chunks (words, arguments) |
this.argQL: |
Array of quoting level per argument
This array will contain the according quote character or an empty string for each element of argv with the same index. |
this.argc: |
A pointer to this.argv and this.argQL (initially set to 0; used by method getopt). |
Example:
For the string: | | This is "quoted". |
argv will result to: | | ['this', 'is', 'quoted', '.'] |
and argQL will result to: | | ['', '', '"', ''] |
(Provided that '"' is defined as a quote character. Defaults: ', ", and `)
getopt(this, "<options>"):
Call this method from inside of your handler to parse any options from the
next chunk in this.argv that this.argc points to.
Options are considered any arguments preceded by an option character in the
property optionChars (default: "-"). The second argument is a string
containing all characters to be considered legal option flags.
The method returns an object with a property of type object for any option
flag (defined as a character in the options-string) found in argv.
argc will be advanced to the first element of argv that is not an option.
Each of the flag-objects will contain the property `value'.
In case that the option flag was immediately followed by a number
(unsigned float value), this will be stored as a Number in the value,
otherwise the flag's value will be set to -1.
Any flags that were not defined in the options-string will be stored in
an array of single characters in the property `illegals' of the returned object.
Example:
this.argv: ["test", "-en7a", "-f", "next"]
this.argc: set initially to 0
var command = this.argv[this.argc++]; // this.argc now points to 2nd chunk
var options = parser.getopt(this, "en");
getopt will return the following object:
{
'e': { value: -1 },
'n': { value: 7 },
'illegals': ['a', 'f']
}
and this.argc will be set to 3, pointing to "next".
Note: Quoted terms are never parsed as options.
Escape expressions:
The parser can handle escape sequences, e.g.: hexdecimal notations of
characters that are preceded by an escape character. (default: parse any
2-byte hex expressions preceded by "%" as a character, e.g. "%41" => "A".)
Escape characters are defined in the property escapeExpressions (type object) with the
according method as their value. (The given function or method must be present
at creation and takes four arguments: terminal instance, current char-position,
the escape character found, and the current quoting level. The method or function
is expected to strip any escape sequence of the lineBuffer and to return a string
to be inserted at the current parsing position.)
The result of an escape expression will allways add to the current chunk and will never result in parsed white space.
Configuration:
You may want to overide the follow objects (or add properties):
parser.whiteSpace: | chars to be parsed as white space (default: space, tab) |
parser.quoteChars: | chars to be parsed as quotes (default: ", ', `) |
parser.singleEscapes: | chars to escape a quote or escape expression (default: \) |
parser.optionChars: | chars that start an option (default: -) |
parser.escapeExpressions: | chars that start escape expressions (default: % => 2-byte hex) |
Notes:
v. 1.1: Parser is now a function with a constructor.
Configurations can be handled at a per-instance basis.
Parser is now a single, self-contained object in the global name space.
Note on the implementation: We are not storing the terminal instance in order to avoid memory leakage.
Alternate Implementation Example – not UNIX-like: (for a sane implementation see the source of this file.)
// a simple command shell,
// toggeling "-" (minus) and "/" (forward slash) as the option character
var term, parser;
var files = {
"initscreen": [
"*** Quick and Dumb Shell v. 1.0 ***",
"Nanofluffy Corporation 1982",
"ready.",
"type 'help' for a list of commands."
],
"help": [
"Commands:",
" type [-p] <filename> .... write a file to the screen",
" option p: use screen pager",
" print <filename> [/p] ... write a file to the screen",
" option p: use screen pager",
" help .................... display this screen",
" exit .................... exit the command shell"
],
"test": [
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam",
"facilisis enim. Pellentesque in elit et lacus euismod dignissim.",
"Aliquam dolor pede, convallis eget, dictum a, blandit ac, urna.",
"Pellentesque sed nunc ut justo volutpat egestas. Class aptent",
"taciti sociosqu ad litora torquent per conubia nostra, per inceptos",
"hymenaeos. In erat. Suspendisse potenti. Fusce faucibus nibh sed",
"nisi. Phasellus faucibus, dui a cursus dapibus, mauris nulla",
"euismod velit, a lobortis turpis arcu vel dui. Pellentesque",
"fermentum ultrices pede. Donec auctor lectus eu arcu. Curabitur",
"non orci eget est porta gravida. Aliquam pretium orci id nisi.",
"Duis faucibus, mi non adipiscing venenatis, erat urna aliquet",
"elit, eu fringilla lacus tellus quis erat. Nam tempus ornare",
"lorem. Nullam feugiat. Lorem ipsum dolor sit amet, consectetuer",
"adipiscing elit. Nam facilisis enim. Pellentesque in elit et lacus",
"euismod dignissim. Aliquam dolor pede, convallis eget, dictum a,",
"blandit ac, urna. Pellentesque sed nunc ut justo volutpat egestas.",
"Class aptent taciti sociosqu ad litora torquent per conubia nostra,",
"per inceptos hymenaeos. In erat. Suspendisse potenti. Fusce",
"faucibus nibh sed nisi. Phasellus faucibus, dui a cursus dapibus,",
"mauris nulla euismod velit, a lobortis turpis arcu vel dui.",
"Pellentesque fermentum ultrices pede. Donec auctor lectus eu arcu.",
"Curabitur non orci eget est porta gravida. Aliquam pretium orci id",
"nisi. Duis faucibus, mi non adipiscing venenatis, erat urna aliquet",
"elit, eu fringilla lacus tellus quis erat. Nam tempus ornare lorem.",
"Nullam feugiat. Lorem ipsum dolor sit amet, consectetuer adipiscing",
"elit. Nam facilisis enim. Pellentesque in elit et lacus euismod",
"dignissim. Aliquam dolor pede, convallis eget, dictum a, blandit ac,",
"urna. Pellentesque sed nunc ut justo volutpat egestas. Class aptent",
"taciti sociosqu ad litora torquent per conubia nostra, per inceptos",
"hymenaeos. In erat. Suspendisse potenti. Fusce faucibus nibh sed nisi.",
"Phasellus faucibus, dui a cursus dapibus, mauris nulla euismod velit,",
"a lobortis dui: The quick brown fox jumps over the lazy dog."
]
};
function openTerminal() {
if (!term || term.closed) {
// create and open a terminal
term = new Terminal(
{
handler: commandShell,
termDiv: "termDiv",
ps: ">",
greeting: files.initscreen
}
);
term.open();
// create and configure the parser
parser = new Parser();
// only accept double and single quotes as quote characters
parser.quoteChars = { "\"": true, "'": true };
// set "-" (minus) as the option character (same as default)
parser.optionChars = { "-": true };
}
}
function commandShell() {
this.newLine();
parser.parseLine(this);
if (this.argv.length == 0) {
// no commmand line input
}
else {
var cmd = this.argv[this.argc++]; // we advance argc to point to the next chunk
cmd = cmd.toLowerCase(); // case insensitive (dumb shell)
// now handle each command
switch (cmd) {
// "system" commands
case "help":
// display a help screen
this.clear();
this.write(files.help);
break;
case "exit":
// just quit with grace
this.close();
return;
// implement a command "type [-p] <filename>"
case "type":
// read option "p"
var opts = parser.getopt(this, "p");
if (opts.illegals.length) {
// other option flags found
this.write("Error: Illegal option.");
}
else if (this.argc != this.argv.length-1) {
this.write("Error: Illegal argument list.");
}
else {
var filename = this.argv[this.argc];
var file = files[filename];
if (file) {
if (opts.p) {
// write in more-mode
this.write(file, true);
return;
}
else {
this.write(file);
}
}
else {
this.write("Error: No such file '"+filename+"'.");
}
}
break;
// implement a command "print <filename> [/p]"
case "print":
if (this.argv.length>this.argc) {
var filename = this.argv[this.argc];
if (this.argQL[this.argc] || filename.charAt(0) != "/") {
// filename quoted or not an option => suppose it's legal
// advance argc
this.argc++;
// get option /p
// overwrite default optionChars with forward slash
parser.optionChars = { "/": true };
var opts = parser.getopt(this, "p");
// now reset the optionChars
parser.optionChars = { "-": true };
// check and execute ...
if (opts.illegals.length) {
this.write("Error: Illegal option.");
}
else {
var file = files[filename];
if (file) {
if (opts.p) {
this.write(file, true);
return;
}
else {
this.write(file);
}
}
else {
this.write("Error: No such file '"+filename+"'.");
}
}
}
else {
this.write("Error: Illegal argument list.");
}
}
else {
this.write("Error: Missing argument list.");
}
break;
// not a known command
default:
this.write("Not a command: "+cmd);
}
}
this.prompt();
}
// eof
|