diff --git a/mock-census/README.md b/mock-census/README.md new file mode 100644 index 0000000..734e53e --- /dev/null +++ b/mock-census/README.md @@ -0,0 +1,59 @@ +CS 142 Mid-Term 2 (Fall 2010) +==== + +**Mock Census Parser** + +Must read a file via HTTP or on the local filesystem. + +(The file has ostensibly just passed validation and needs no further error-checking) + +The file contains three newline-delimited directives per block and one EOF directive. + +Each directive has a number of space-delimited attributes. + +The directives appear in this order: + + * `Head` - this must be the start of the block. Represents the head of household + * name - first name of the individual + * gender + * eye_color + * hair_color + * height - feet as a float + + * `Depedent` - 0 or many may exist. Represents children, spouse, etc + * name + * gender + * age - 'minor' or 'adult' + * height + + * `End` - must close the block before the next 'Head' + + * `SUPER-END` - a special end-of-file (EOF) marker + * `end` MUST precede `SUPER-END` + +Example Data File +==== + + Head Kevin M brown brown 6.15 + Dependent Dagmar F adult 5.9 + Dependent Dorkus M minor 5.7 + End + Head Eve F blue brown 5.5 + Dependent Adam M adult 6.2 + Dependent Fred M minor 6.1 + Dependent Fredwenda F adult 5.3 + End + Head Pentultimus M brown brown 6 + End + Head Beautisha F green red 5.7 + Dependent Bodacious M minor 4.0 + End + SUPER-END + +Variations from the Spec +==== + +My implementation is written in JavaScript (which is like LISP, not at all like Java). +I take advantage of some JavaScript-isms. + +In my implementation I also handle `#` comments as well as some extremely minimalistic error-checking. diff --git a/mock-census/mock-census-test.sh b/mock-census/mock-census-test.sh new file mode 100644 index 0000000..adeded3 --- /dev/null +++ b/mock-census/mock-census-test.sh @@ -0,0 +1,9 @@ +# invalid +node mock-census.js +node mock-census.js files:///path/to/file +node mock-census.js httpss://domain.tld/resource +# valid +node mock-census.js /path/to/file +node mock-census.js file:///path/to/file +node mock-census.js http://domain.tld/resource +node mock-census.js https://domain.tld/resource diff --git a/mock-census/mock-census.js b/mock-census/mock-census.js new file mode 100644 index 0000000..72ae2e4 --- /dev/null +++ b/mock-census/mock-census.js @@ -0,0 +1,191 @@ +// TODO parse streaming +"use strict"; +(function (undefined) { + var fs = require('fs'), + url = require('url'), + ahr = require('ahr'), + stream, + node = process.argv[0], + self = process.argv[1], + fulluri = process.argv[2], + uri; + + self = self.substr(self.lastIndexOf('/') + 1); + + require('remedial'); + + function usage() { + console.log("Usage: node {self}".supplant({self: self}) + + " file:///path/to/file | http://path/to/file"); + } + + + // Head, and Dependent "Classes" + function Comment(lines) { + while (true) { + line = lines.shift(); + if (undefined === line || null === line) { + throw new Error("Expected 'Dependent' or 'end' but reach end-of-file"); + } + if (!line.match(/\s*#/)){ + break; + } + } + return line; + } + + function Dependent(lines) { + var line, dep; + line = Comment(lines); + + if (line.match(/^Dependent\s*/)) { + parts = line.split(/\s+/); + dep = { + // no error checking in spec + name: parts[1], + gender: parts[2], + age: parts[3], + height: parseInt(parts[4], 10), + }; + } else if (line.match(/^End\s*/)) { + return false; + } else { + throw new Error("Expected 'Dependent' or 'End' but saw\n " + line.substr(0,20)); + } + return dep; + } + + function Head(lines) { + var line, head, parts, dep; + line = Comment(lines); + + if (line.match(/^Head\s*/)) { + parts = line.split(/\s+/); + head = { + // no error checking in spec + name: parts[1], + gender: parts[2], + eye_color: parts[3], + hair_color: parts[4], + height: parseInt(parts[5], 10), + deps: [] + }; + while (dep = Dependent(lines)) { + head.deps.push(dep); + } + } else if (line.match(/^SUPER-END\s*/)) { + return false; + } else { + throw new Error("Expected 'Head' or 'SUPER-END' but saw\n " + line.substr(0,20)); + } + return head; + } + + function readFile(file) { + var promise = require('futures').promise(), + stream, + data = new Buffer(''); + + stream = fs.createReadStream(file, { flags: 'r' }); + stream.on('error', function (err) { + promise.fulfill(err, stream, data); + }); + stream.on('data', function (chunk) { + data += chunk; + }); + stream.on('end', function () { + promise.fulfill(undefined, stream, data); + }); + return promise; + } + + // Queries + + // Average height of Heads of Household + function query1(heads) { + var heights = 0.0; + heads.forEach(function (head) { + heights += head.height; + }); + console.log("Average Height of Heads of Household: " + (heights / heads.length)); + } + + // What is the height of the tallest of all dependents? + function query2(heads) { + var max = 0.0; + heads.forEach(function (head) { + head.deps.forEach(function (dep) { + max = Math.max(max, dep.height); + }); + }); + console.log("Height of tallest Dependent: " + max); + } + + // The height of the shortest Dependent of a female Head of Household + function query3(heads) { + var min = Infinity; + heads.forEach(function (head) { + if ('F' !== head.gender) { + return; + } + head.deps.forEach(function (dep) { + min = Math.min(min, dep.height); + }); + }); + console.log("Height of shortest Dependent of a Female-Headed Household: " + min); + } + + // Average height of male dependents of brown-haired male heads of houshold + function query4(heads) { + var heights = 0.0, count = 0; + heads.forEach(function (head) { + if ('m' !== head.gender && 'brown' !== head.hair_color) { + return; + } + head.deps.forEach(function (dep) { + count += 1; + heights += dep.height; + }); + }); + console.log("Average height of male dependent of brown-haired male Heads of Household: " + (heights / count)); + } + + function parseData(err, x, data) { + var lines = data.split('\n'), + heads = [], + curHead; + + while (curHead = Head(lines)) { + heads.push(curHead); + } + console.log(JSON.stringify(heads, null, ' ')); + query1(heads); + query2(heads); + query3(heads); + query4(heads); + return heads; + } + + function main() { + if (!fulluri) { + usage(); + return; + } + + uri = url.parse(fulluri, true); + if (!uri.protocol) { + uri.protocol = ''; + } + if ('file:' === uri.protocol || '' === uri.protocol) { + readFile(uri.pathname).when(parseData); + } else if (uri.protocol.match(/^http[s]?:$/)) { + ahr.get(fulluri).when(parseData); + } else { + usage(); + return; + } + } + + main(); + +}()); diff --git a/mock-census/mock-census.txt b/mock-census/mock-census.txt new file mode 100644 index 0000000..a9aceea --- /dev/null +++ b/mock-census/mock-census.txt @@ -0,0 +1,19 @@ +#Head First Gender Eye-color Hair-color Height (exactly one per block) +Head Kevin M brown brown 6.15 +#Dependent First Gender Age Height (0 or more per block) +Dependent Dagmar F adult 5.9 +Dependent Dorkus M minor 5.7 +#End end-of-block +End +Head Eve F blue brown 5.5 +Dependent Adam M adult 6.2 +Dependent Fred M minor 6.1 +Dependent Fredwenda F adult 5.3 +End +Head Pentultimus M brown brown 6 +End +Head Beautisha F green red 5.7 +Dependent Bodacious M minor 4.0 +End +#SUPER-END end-of-file +SUPER-END