Skip to content

Instantly share code, notes, and snippets.

@joeyadams
Created September 27, 2012 20:20
Show Gist options
  • Save joeyadams/3796234 to your computer and use it in GitHub Desktop.
Save joeyadams/3796234 to your computer and use it in GitHub Desktop.
#!/bin/bash
if [ $# -lt 1 ] ; then
echo "Usage: $0 ./command" >& 2
exit 1
fi
if [ $# -eq 1 ] ; then
if [ ! -f "$(which "$1" 2>/dev/null)" ] ; then
if [ -f "./$1" ] ; then
echo "$1 not found. Perhaps you meant:" >& 2
echo >& 2
printf '\t'
echo "$0 ./$1" >& 2
echo >& 2
else
echo "$1 not found." >& 2
fi
exit 1
fi
fi
command="$@"
score=0
total=0
function expect {
out="$(echo -n "$2" | $command)"
if [ "$out" = "$1" ] ; then
: $((score++))
else
echo "fail: should be $(echo "$1" | tr A-Z a-z): $2" >& 2
fi
: $((total++))
}
function expect_printf {
expected="$1"
shift
out="$(printf "$@" | $command)"
if [ "$out" = "$expected" ] ; then
: $((score++))
else
echo "fail: should be $(echo "$expected" | tr A-Z a-z): E'$@'" >& 2
fi
: $((total++))
}
expect_printf Valid '[\n]'
expect_printf Invalid '["\n"]'
expect_printf Valid '["\\n"]'
expect_printf Invalid '["\t"]'
expect_printf Valid '["\\t"]'
expect_printf Invalid '["\\a"]'
expect_printf Valid '["\\b"]'
expect_printf Valid '\t\n [\n\t 1 \t\n, \n\t2 ]\t'
expect_printf Invalid '[\f]'
expect_printf Invalid '[\v]'
expect_printf Invalid '["\t"]'
expect_printf Invalid '["\v"]'
expect_printf Invalid '["\f"]'
expect_printf Invalid '["\n"]'
expect_printf Invalid '["\r"]'
expect_printf Valid '[" "]'
expect_printf Valid '[" "] '
expect_printf Valid ' [" "]'
expect_printf Invalid ' [" "]]'
expect_printf Invalid '[]\x00'
expect_printf Valid '[]\n'
expect_printf Invalid '["""]'
expect_printf Invalid '["\\"]'
expect_printf Invalid '["\x00"]'
expect_printf Invalid '["\x01"]'
expect_printf Invalid '["\x02"]'
expect_printf Invalid '["\x03"]'
expect_printf Invalid '["\x04"]'
expect_printf Invalid '["\x05"]'
expect_printf Invalid '["\x06"]'
expect_printf Invalid '["\x07"]'
expect_printf Invalid '["\x08"]'
expect_printf Invalid '["\x09"]'
expect_printf Invalid '["\x0A"]'
expect_printf Invalid '["\x0B"]'
expect_printf Invalid '["\x0C"]'
expect_printf Invalid '["\x0D"]'
expect_printf Invalid '["\x0E"]'
expect_printf Invalid '["\x0F"]'
expect_printf Invalid '["\x10"]'
expect_printf Invalid '["\x11"]'
expect_printf Invalid '["\x12"]'
expect_printf Invalid '["\x13"]'
expect_printf Invalid '["\x14"]'
expect_printf Invalid '["\x15"]'
expect_printf Invalid '["\x16"]'
expect_printf Invalid '["\x17"]'
expect_printf Invalid '["\x18"]'
expect_printf Invalid '["\x19"]'
expect_printf Invalid '["\x1A"]'
expect_printf Invalid '["\x1B"]'
expect_printf Invalid '["\x1C"]'
expect_printf Invalid '["\x1D"]'
expect_printf Invalid '["\x1E"]'
expect_printf Invalid '["\x1F"]'
expect Valid '["\\"]'
expect Valid '["\/"]'
expect Valid '["\""]'
expect Valid ' [ -1 ] '
expect Invalid '-1'
expect Invalid ' [ - 1 ] '
expect Valid ' [ 1 ] '
expect Valid ' [ 12 ] '
expect Valid ' [ 123 ] '
expect Invalid ' [ 1 23 ] '
expect Valid ' [ 0 ] '
expect Invalid ' [ 01 ] '
expect Invalid ' [ \x12 ] '
expect Valid ' [ 0.1 ] '
expect Invalid ' [ 01.2 ] '
expect Valid ' [ 1.1 ] '
expect Invalid ' [ .1 ] '
expect Valid ' [ 0.01 ] '
expect Valid ' [ 0.10 ] '
expect Valid ' [ 0.1e0 ] '
expect Valid ' [ 0.1e012 ] '
expect Invalid ' [ 0.1e ] '
expect Invalid ' [ 0.e1 ] '
expect Valid ' [ 0.1e+1 ] '
expect Valid ' [ 0.1e-1 ] '
expect Invalid ' [ 0.1e+-1 ] '
expect Invalid ' [ 0.1e-+1 ] '
expect Valid '[1,2,3]'
expect Valid '[1 , 2 , 3 ]'
expect Valid '[0.1,2]'
expect Valid '[0.1e2,3]'
expect Invalid ''
expect Invalid ' '
expect Invalid '"'
expect Invalid '[,]'
expect Invalid '[)'
expect Invalid '[]]'
expect Invalid '[}'
expect Invalid '{,}'
expect Invalid '{]'
expect Invalid '["1":2]'
expect Invalid '[1,2,]'
expect Invalid '[1:2}'
expect Invalid '{"1":2,}'
expect Invalid '{1:2}'
expect Invalid '{"1":2, "2.5" : [3, 4, {}, {"5": ["6"], [7 ]}]}'
expect Invalid '{"1":2, "2.5" : [3, 4, {}, {"5": ["6"], [7]}]}'
expect Invalid '{"1":2, "2.5" : [3, 4, {}, {"5": ["6"], "7" :[8 ]}]'
expect Invalid '{"1":2, "2.5" : [3, 4, {}, {"5": ["6"], "7" :[8 ]}]]'
expect Invalid '{"1":2, "3":4'
expect Invalid '["1\u2"]'
expect Invalid '[,2]'
expect Invalid '["3]'
expect Invalid '["3" "4"]'
expect Invalid '[3[4]'
expect Invalid '[3[4]]'
expect Invalid '[3, [4, [5], 6] 7, 8 9]'
expect Invalid '[3, [4, [5], 6] 7, 8, 9]'
expect Invalid '[3, [4, [5], 6], 7, 8 9]'
expect Invalid '{"hello":true, "bye":false, null}'
expect Invalid '{"hello":true, "bye":false, null:null}'
expect Invalid '["hi]'
expect Invalid '["hi"""]'
expect Invalid '{"hi": "bye"]'
expect Valid '["\uFFFE"]'
expect Valid '["\uFFFF"]'
expect Invalid '[.]'
expect Valid '[""]'
expect Valid '[]'
expect Valid '{}'
expect Invalid '[+.]'
expect Valid '[0.5]'
expect Valid '[0.5 ]'
expect Valid '[ 0.5]'
expect Invalid '[0 .5]'
expect Invalid '[0. 5]'
expect Invalid '0.e1'
expect Valid '{"1":{}}'
expect Valid '{"1":2}'
expect Valid '{"1":2, "2.5" : [3, 4, {}, {"5": ["6"]}]}'
expect Valid '{"1":2, "2.5" : [3, 4, {}, {"5": ["6"], "7" :[8 ]}]}'
expect Valid '[1234]'
expect Valid '[-1234]'
expect Valid '{"1":2, "3":4}'
expect Invalid '[+1234]'
expect Invalid '[++1234]'
expect Valid '[123.456e14234]'
expect Valid '[123.456e-14234]'
expect Valid '[123.456e+14234]'
expect Invalid '[123.e-14234]'
expect Valid '["1\u2000"]'
expect Valid '["1\u20001"]'
expect Valid '[2]'
expect Invalid '[.246e-14234]'
expect Invalid '[.2e-14234]'
expect Valid '[3]'
expect Invalid '[.3]'
expect Valid '["3"]'
expect Valid '[3]'
expect Invalid '[+3.]'
expect Valid '[3.2e+1]'
expect Valid '[3, [4]]'
expect Valid '[3, [4, [5]]]'
expect Valid '[3, [4, [5], 6]]'
expect Valid '[3, [4, [5], 6], 7]'
expect Valid '[3, [4, [5], 6], 7, 8]'
expect Valid '[3, [4, [5], 6], 7, 8, 9]'
expect Invalid '[+3.5]'
expect Invalid '[.3e]'
expect Invalid '[.3e1]'
expect Invalid '[.3e-1]'
expect Invalid '[.3e+1]'
expect Invalid '[3.e1]'
expect Invalid '[3.e+1]'
expect Valid '[3e+1]'
expect Invalid '[.5]'
expect Invalid '[+.5]'
expect Invalid '[.5e+1]'
expect Valid '[ 7]'
expect Valid '[7 ]'
expect Valid '[7]'
expect Invalid '[.e-14234]'
expect Valid '["hello"]'
expect Valid '["hello"]'
expect Valid '["hello", "bye"]'
expect Valid '["hello", "bye\n"]'
expect Valid '["hello", "bye\n\r\t"]'
expect Valid '["hello", "bye\n\r\t\b"]'
expect Valid '["hello", "bye\n\r\t\b",true]'
expect Valid '["hello", "bye\n\r\t\b",true , false]'
expect Valid '["hello", "bye\n\r\t\b",true , false, null]'
expect Invalid '["hello", "bye\n\r\t\v"]'
expect Valid '{"hello":true}'
expect Valid '{"hello":true, "bye":false}'
expect Valid '{"hello":true, "bye":false, "foo":["one","two","three"]}'
expect Valid '["hi"]'
expect Valid '["hi"]'
expect Valid '["hi", "bye"]'
expect Valid '{"hi": "bye"}'
expect Valid '["hi", "bye", 3]'
expect Valid '["hi", "bye[", 3]'
expect Valid '["\u0007"]'
expect Valid '["\u0008"]'
expect Valid '["\u0009"]'
expect Valid '["\u0010"]'
expect Valid '["\u0020"]'
expect Valid '["\u10000"]'
expect Valid '["\u1234"]'
expect Valid '["\u99999"]'
expect Valid '["\ud800\udc00"]'
expect Valid '["\uD800\uDC00"]'
expect Valid '["\uD834\uDD1E"]'
expect Valid '["\uDBFF\uDFFF"]'
expect Valid '["\uFFFD"]'
expect Valid '["\uFFFF"]'
expect Invalid '[hello]'
expect Valid '[32, 1]'
expect Invalid '[32, '
expect Valid '["\uD800\uDC00"]'
expect Valid '["\n"]'
expect Valid '["hello"]'
expect Valid '["hello\u0009world"]'
expect Valid '["hello"]'
expect Valid '["hello\n"]'
expect Valid '["hello"]'
expect Valid '[3]'
expect Invalid '[3.]'
expect Invalid '[.3]'
expect Valid '[0.3]'
expect Invalid '[0.3e]'
expect Invalid '[0.3e+]'
expect Valid '[0.3e+5]'
expect Valid '[0.3e-5]'
expect Valid '[0.3e5]'
expect Valid '["hello"]'
expect Invalid '[+3]'
expect Valid '[-3]'
expect Invalid '[-3.]'
expect Valid '[-3.1]'
expect Invalid '[.5]'
expect Invalid '[5.]'
expect Invalid '[5.e1]'
expect Valid '[0.5]'
expect Invalid '[.3e1]'
expect Invalid '[.3e+1]'
expect Invalid '[.3e-1]'
expect Invalid '[.3e-1 .5]'
expect Invalid '[.3e-1.5]'
expect Invalid '[.3e+1.5]'
expect Invalid '[.3e+.]'
expect Invalid '[.3e+.5]'
expect Invalid '[.3e+1.5]'
expect Invalid '[9.3e+1.5]'
expect Invalid '[9.e+1.5]'
expect Invalid '[9.e+]'
expect Invalid '[9.e+1]'
expect Valid '["\""]'
expect Valid '["\"3.5"]'
expect Valid '["\"."]'
expect Invalid '["\".".]'
expect Valid '["\"....."]'
expect Invalid '["\"\"\"\"""]'
expect Invalid '["\"\"\"\"", .5]'
expect Invalid '[.5]'
expect Valid '["\"\"\"\"", 0.5]'
expect Invalid '["\"\"\"\"", .5]'
expect Invalid '["\"\"\"\"",.5]'
expect Invalid '["\"",.5]'
expect Invalid '["\".5",.5]'
expect Invalid '["\".5",".5\"".5]'
expect Invalid '["\".5",".5\"", .5]'
expect Invalid '["\".5",".5\"",.5]'
expect Valid '["\".5",".5\"",0.5]'
expect Invalid '{"key":/*comment*/"value"}'
expect Invalid '{"key":/*comment"value"}'
expect Invalid '{"key":"value"}/*'
expect Invalid '{"key":"value"}/**/'
expect Invalid '{"key":"value"}/***/'
expect Invalid '{"key":"value"}/**//'
expect Invalid '{"key":"value"}/**///'
expect Invalid '{"key":"value"}/**///----'
expect Invalid '{"key":"value"}#'
expect Invalid '{"key":"value"}#{'
expect Invalid '{"key":"value"}#{}'
expect Invalid '{"key":"value"}#,'
expect Invalid '{"key":"value"/**/, "k2":"v2"}'
expect Valid '["\u0027"]'
expect Invalid '["hello\'"'"'"]'
expect Invalid '['"'"'hello\'"'"''"'"']'
expect Invalid '['"'"'hello'"'"']'
expect Invalid '['"'"'hell\'"'"'o'"'"']'
expect Invalid '['"'"'\'"'"'hello'"'"']'
expect Invalid '['"'"'\'"'"'hello\'"'"''"'"']'
expect Invalid '[\'"'"'hello\'"'"']'
expect Invalid '['"'"'hello\'"'"']'
expect Invalid '['"'"'hello\'"'"']'
expect Invalid '['"'"'hello\'"'"''"'"']'
expect Invalid '['"'"'hello"'"'"']'
expect Invalid '['"'"'hello\"'"'"']'
expect Invalid '['"'"'hello"o'"'"']'
expect Invalid '['"'"'"'"'"']'
expect Invalid '['"'"'"'"'"']'
expect Invalid '['"'"'"hello"'"'"']'
expect Invalid '['"'"'"hello'"'"']'
expect Invalid '['"'"'"hi"'"'"']'
echo "score: $score/$total"
/*
* Copyright (C) 2011 Joseph Adams <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
typedef enum {Invalid = 0, Null, Bool, String, Number, Object, Array} Type;
static Type parseValue(int c);
static Type parseArray(int c);
static Type parseObject(int c);
static Type parseString(int c);
static Type parseNumber(int c);
static Type parseLiteral(int c, const char *string, Type on_match);
static int nextToken(void);
static int isDigit(int c);
static int isHexDigit(int c);
int main(void)
{
Type t = parseValue(nextToken());
if ((t == Object || t == Array) && nextToken() == EOF)
puts("Valid");
else
puts("Invalid");
return 0;
}
static Type parseValue(int c)
{
Type t;
/* Try each parser and stop when one of them succeeds. */
(t = parseLiteral(c, "null", Null)) ||
(t = parseLiteral(c, "false", Bool)) ||
(t = parseLiteral(c, "true", Bool)) ||
(t = parseString(c)) ||
(t = parseNumber(c)) ||
(t = parseObject(c)) ||
(t = parseArray(c)) ||
(t = Invalid);
return t;
}
static Type parseArray(int c)
{
if (c != '[')
return Invalid;
if ((c = nextToken()) == ']')
return Array;
for (;;) {
if (parseValue(c) == Invalid)
return Invalid;
c = nextToken();
if (c == ',') {
c = nextToken();
continue;
}
if (c == ']')
return Array;
return Invalid;
}
}
static Type parseObject(int c)
{
if (c != '{')
return Invalid;
if ((c = nextToken()) == '}')
return Object;
for (;;) {
if (parseValue(c) != String)
return Invalid;
if (nextToken() != ':')
return Invalid;
if (parseValue(nextToken()) == Invalid)
return Invalid;
c = nextToken();
if (c == ',') {
c = nextToken();
continue;
}
if (c == '}')
return Object;
return Invalid;
}
}
static Type parseString(int c)
{
if (c != '"')
return Invalid;
while ((c = getchar()) != '"') {
if (c <= 0x1F) /* Control character or EOF. */
return Invalid;
if (c == '\\') {
switch (getchar()) {
case '"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
break;
case 'u':
{
int i;
for (i = 0; i < 4; i++)
if (!isHexDigit(getchar()))
return Invalid;
}
break;
default:
return Invalid;
}
}
}
return String;
}
static Type parseNumber(int c)
{
if (c == '-')
c = getchar();
if (!isDigit(c))
return Invalid;
if (c == '0') {
c = getchar();
} else {
do { c = getchar(); } while (isDigit(c));
}
if (c == '.') {
c = getchar();
if (!isDigit(c))
return Invalid;
do { c = getchar(); } while (isDigit(c));
}
if (c == 'E' || c == 'e') {
c = getchar();
if (c == '+' || c == '-')
c = getchar();
if (!isDigit(c))
return Invalid;
do { c = getchar(); } while (isDigit(c));
}
ungetc(c, stdin);
return Number;
}
static Type parseLiteral(int c, const char *string, Type on_match)
{
for (;;) {
if (*string++ != c)
return Invalid;
if (*string == '\0')
return on_match;
c = getchar();
}
}
static int nextToken(void)
{
int c;
do {
c = getchar();
} while (c == '\t' || c == '\n' || c == '\r' || c == ' ');
return c;
}
static int isDigit(int c)
{
return (c >= '0' && c <= '9');
}
static int isHexDigit(int c)
{
return (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment