2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,10 +1,18 @@
[[wp:S-Expression|S-Expressions]] are one convenient way to parse and store data.
[[wp:S-Expression|S-Expressions]]   are one convenient way to parse and store data.
;Task:
Write a simple reader and writer for S-Expressions that handles quoted and unquoted strings, integers and floats.
The reader should read a single but nested S-Expression from a string and store it in a suitable datastructure (list, array, etc). Newlines and other whitespace may be ignored unless contained within a quoted string. “<tt>()</tt>” inside quoted strings are not interpreted, but treated as part of the string. Handling escaped quotes inside a string is optional; thus “<tt>(foo"bar)</tt>” maybe treated as a string “<tt>foo"bar</tt>”, or as an error.
The reader should read a single but nested S-Expression from a string and store it in a suitable datastructure (list, array, etc).
For this, the reader need not recognise “<tt>\</tt>” for escaping, but should, in addition, recognize numbers if the language has appropriate datatypes.
Newlines and other whitespace may be ignored unless contained within a quoted string.
“<tt>()</tt>” &nbsp; inside quoted strings are not interpreted, but treated as part of the string.
Handling escaped quotes inside a string is optional; &nbsp; thus “<tt>(foo"bar)</tt>” maybe treated as a string “<tt>foo"bar</tt>”, or as an error.
For this, the reader need not recognize “<tt>\</tt>” for escaping, but should, in addition, recognize numbers if the language has appropriate datatypes.
Languages that support it may treat unquoted strings as symbols.
@ -18,4 +26,7 @@ and turn it into a native datastructure. (see the [[#Pike|Pike]], [[#Python|Pyth
The writer should be able to take the produced list and turn it into a new S-Expression.
Strings that don't contain whitespace or parentheses () don't need to be quoted in the resulting S-Expression, but as a simplification, any string may be quoted.
'''Extra Credit:''' Let the writer produce pretty printed output with indenting and line-breaks
;Extra Credit:
Let the writer produce pretty printed output with indenting and line-breaks.
<br><br>

View file

@ -0,0 +1,127 @@
# S-Expressions #
CHAR nl = REPR 10;
# mode representing an S-expression #
MODE SEXPR = STRUCT( UNION( VOID, STRING, REF SEXPR ) element, REF SEXPR next );
# creates an initialises an SEXPR #
PROC new s expr = REF SEXPR: HEAP SEXPR := ( EMPTY, NIL );
# reports an error #
PROC error = ( STRING msg )VOID: print( ( "**** ", msg, newline ) );
# S-expression reader - reads and returns an S-expression from the string s #
PROC s reader = ( STRING s )REF SEXPR:
BEGIN
PROC at end = BOOL: s pos > UPB s;
PROC curr = CHAR: IF at end THEN REPR 0 ELSE s[ s pos ] FI;
PROC skip spaces = VOID: WHILE NOT at end AND ( curr = " " OR curr = nl ) DO s pos +:= 1 OD;
PROC end of list = BOOL: at end OR curr = ")";
INT s pos := LWB s;
INT t pos;
[ ( UPB s - LWB s ) + 1 ]CHAR token; # token text - large enough to hold the whole string if necessary #
# adds the current character to the token #
PROC add curr = VOID: token[ t pos +:= 1 ] := curr;
# get an s expression element from s #
PROC get element = REF SEXPR:
BEGIN
REF SEXPR result = new s expr;
skip spaces;
# get token text #
IF at end THEN
# no element #
element OF result := EMPTY
ELIF curr = "(" THEN
s pos +:= 1;
skip spaces;
IF NOT end of list
THEN
REF SEXPR nested expression = get element;
REF SEXPR element pos := nested expression;
element OF result := nested expression;
skip spaces;
WHILE NOT end of list
DO
element pos := next OF element pos := get element;
skip spaces
OD
FI;
IF curr = ")" THEN
s pos +:= 1
ELSE
error( "Missing "")""" )
FI
ELIF curr = ")" THEN
s pos +:= 1;
error( "Unexpected "")""" );
element OF result := EMPTY
ELSE
# quoted or unquoted string #
t pos := LWB token - 1;
IF curr /= """" THEN
# unquoted string #
WHILE add curr;
s pos +:= 1;
NOT at end AND curr /= " " AND curr /= "("
AND curr /= ")" AND curr /= """"
AND curr /= nl
DO SKIP OD
ELSE
# quoted string #
WHILE add curr;
s pos +:= 1;
NOT at end AND curr /= """"
DO SKIP OD;
IF curr /= """" THEN
# missing string quote #
error( "Unterminated string: <<" + token[ : t pos ] + ">>" )
ELSE
# have the closing quote #
add curr;
s pos +:= 1
FI
FI;
element OF result := token[ : t pos ]
FI;
result
END # get element # ;
REF SEXPR s expr = get element;
skip spaces;
IF NOT at end THEN
# extraneuos text after the expression #
error( "Unexpected text at end of expression: " + s[ s pos : ] )
FI;
s expr
END # s reader # ;
# prints an S expression #
PROC s writer = ( REF SEXPR s expr )VOID:
BEGIN
# prints an S expression with a suitable indent #
PROC print indented s expression = ( REF SEXPR s expr, INT indent )VOID:
BEGIN
REF SEXPR s pos := s expr;
WHILE REF SEXPR( s pos ) ISNT REF SEXPR( NIL ) DO
FOR i TO indent DO print( ( " " ) ) OD;
CASE element OF s pos
IN (VOID ): print( ( "()", newline ) )
, (STRING s): print( ( s, newline ) )
, (REF SEXPR e): BEGIN
print( ( "(", newline ) );
print indented s expression( e, indent + 4 );
FOR i TO indent DO print( ( " " ) ) OD;
print( ( ")", newline ) )
END
OUT
error( "Unexpected S expression element" )
ESAC;
s pos := next OF s pos
OD
END # print indented s expression # ;
print indented s expression( s expr, 0 )
END # s writer # ;
# test the eader and writer with the example from the task #
s writer( s reader( "((data ""quoted data"" 123 4.5)"
+ nl
+ " (data (!@# (4.5) ""(more"" ""data)"")))"
+ nl
)
)

View file

@ -1,8 +1,8 @@
import Text.ParserCombinators.Parsec ((<|>), (<?>), many, many1, char, try, parse, sepBy, choice)
import Text.ParserCombinators.Parsec.Char (noneOf)
import Text.ParserCombinators.Parsec.Token (integer, float, whiteSpace, stringLiteral, makeTokenParser)
import Text.ParserCombinators.Parsec.Language (haskellDef)
import Data.Functor
import Text.Parsec ((<|>), (<?>), many, many1, char, try, parse, sepBy, choice)
import Text.Parsec.Char (noneOf)
import Text.Parsec.Token (integer, float, whiteSpace, stringLiteral, makeTokenParser)
import Text.Parsec.Language (haskell)
data Val = Int Integer
| Float Double
@ -10,40 +10,19 @@ data Val = Int Integer
| Symbol String
| List [Val] deriving (Eq, Show)
lexer = makeTokenParser haskellDef
tInteger = (integer lexer) >>= (return . Int) <?> "integer"
tFloat = (float lexer) >>= (return . Float) <?> "floating point number"
tString = (stringLiteral lexer) >>= (return . String) <?> "string"
tSymbol = (many1 $ noneOf "()\" \t\n\r") >>= (return . Symbol) <?> "symbol"
tAtom = choice [try tFloat, try tInteger, tSymbol, tString] <?> "atomic expression"
tExpr = do
whiteSpace lexer
expr <- tList <|> tAtom
whiteSpace lexer
return expr
<?> "expression"
tList = do
char '('
list <- many tExpr
char ')'
return $ List list
<?> "list"
tProg = many tExpr <?> "program"
where tExpr = between ws ws (tList <|> tAtom) <?> "expression"
ws = whiteSpace haskell
tAtom = try (Int <$> integer haskell) <?> "integer"
<|> try (Float <$> float haskell) <?> "floating point number"
<|> String <$> stringLiteral haskell <?> "string"
<|> Symbol <$> many1 (noneOf "()\"\t\n\r") <?> "symbol"
<?> "atomic expression"
tList = List <$> between (char '(') (char ')') (many tExpr) <?> "list"
p ex = case parse tProg "" ex of
Right x -> putStrLn $ unwords $ map show x
Left err -> print err
p = either print (putStrLn . unwords . map show) . parse tProg ""
main = do
let expr = "((data \"quoted data\" 123 4.5)\n (data (!@# (4.5) \"(more\" \"data)\")))"
putStrLn $ "The input:\n" ++ expr ++ "\n"
putStr "Parsed as:\n"
putStrLn ("The input:\n" ++ expr ++ "\nParsed as:")
p expr

View file

@ -1,34 +1,55 @@
use Text::Balanced qw(extract_delimited extract_bracketed);
#!/usr/bin/perl -w
use strict;
use warnings;
sub sexpr
{
my $txt = $_[0];
$txt =~ s/^\s+//s;
$txt =~ s/\s+$//s;
$txt =~ /^\((.*)\)$/s or die "Not an s-expression: <<<$txt>>>";
$txt = $1;
my @stack = ([]);
local $_ = $_[0];
my $ret = [];
my $w;
while ($txt ne '') {
my $c = substr $txt,0,1;
if ($c eq '(') {
($w, $txt) = extract_bracketed($txt, '()');
$w = sexpr($w);
} elsif ($c eq '"') {
($w, $txt) = extract_delimited($txt, '"');
$w =~ s/^"(.*)"/$1/;
while (m{
\G # start match right at the end of the previous one
\s*+ # skip whitespaces
# now try to match any of possible tokens in THIS order:
(?<lparen>\() |
(?<rparen>\)) |
(?<FLOAT>[0-9]*+\.[0-9]*+) |
(?<INT>[0-9]++) |
(?:"(?<STRING>([^\"\\]|\\.)*+)") |
(?<IDENTIFIER>[^\s()]++)
# Flags:
# g = match the same string repeatedly
# m = ^ and $ match at \n
# s = dot and \s matches \n
# x = allow comments within regex
}gmsx)
{
die "match error" if 0+(keys %+) != 1;
my $token = (keys %+)[0];
my $val = $+{$token};
if ($token eq 'lparen') {
my $a = [];
push @{$stack[$#stack]}, $a;
push @stack, $a;
} elsif ($token eq 'rparen') {
pop @stack;
} else {
$txt =~ s/^(\S+)// and $w = $1;
push @{$stack[$#stack]}, bless \$val, $token;
}
push @$ret, $w;
$txt =~ s/^\s+//s;
}
return $ret;
return $stack[0]->[0];
}
sub quote
{ (local $_ = $_[0]) =~ /[\s\"\(\)]/s ? do{s/\"/\\\"/gs; qq{"$_"}} : $_; }
sub sexpr2txt
{ qq{(@{[ map { ref($_) eq '' ? quote($_) : sexpr2txt($_) } @{$_[0]} ]})} }
{
qq{(@{[ map {
ref($_) eq '' ? quote($_) :
ref($_) eq 'STRING' ? quote($$_) :
ref($_) eq 'ARRAY' ? sexpr2txt($_) : $$_
} @{$_[0]} ]})}
}

View file

@ -1,24 +1,24 @@
/*REXX program parses an S-expression and displays the results. */
/*REXX program parses an S-expression and displays the results. */
input= '((data "quoted data" 123 4.5) (data (!@# (4.5) "(more" "data)")))'
say 'input:'; say input /*display the input data string to term*/
say copies('',length(input)) /*also, display a header fence. */
groupO.= /*default value for grouping symbols. */
groupO.1 = '{' ; groupC.1 = '}' /*grouping symbols (Open & Close). */
groupO.2 = '[' ; groupC.2 = ']' /* " " " " " */
groupO.3 = '(' ; groupC.3 = ')' /* " " " " " */
# = 0 /*the number of tokens found (so far). */
tabs = 10 /*used for the indenting of the levels.*/
q.1 = "'" /*literal string delimiter, first. */
q.2 = '"' /* " " " second. */
numLits = 2 /*the number of kinds of literals. */
seps = ',;' /*characters used for separation. */
atoms = ' 'seps /*characters used to separate atoms. */
level = 0 /*the current level being processed. */
quoted = 0 /*quotation level (when nested). */
groupu = /*used to go ↑ an expression level. */
groupd = /* " " " ↓ " " " */
$.= /*the stem array to hold the tokens. */
do n=1 while groupO.n\=='' /*handle the number of grouping symbols*/
say 'input:'; say input /*display the input data string to term*/
say copies('', length(input)) /*also, display a header fence. */
groupO.= /*default value for grouping symbols. */
groupO.1 = '{' ; groupC.1 = "}" /*grouping symbols (Open & Close). */
groupO.2 = '[' ; groupC.2 = "]" /* " " " " " */
groupO.3 = '(' ; groupC.3 = ")" /* " " " " " */
# = 0 /*the number of tokens found (so far). */
tabs = 10 /*used for the indenting of the levels.*/
q.1 = "'" /*literal string delimiter, first. */
q.2 = '"' /* " " " second. */
numLits = 2 /*the number of kinds of literals. */
seps = ',;' /*characters used for separation. */
atoms = ' 'seps /*characters used to separate atoms. */
level = 0 /*the current level being processed. */
quoted = 0 /*quotation level (when nested). */
groupu = /*used to go ↑ an expression level. */
groupd = /* " " " ↓ " " " */
$.= /*the stem array to hold the tokens. */
do n=1 while groupO.n\=='' /*handle the number of grouping symbols*/
atoms =atoms || groupO.n || groupC.n
groupu=groupu || groupO.n
groupd=groupd || groupC.n
@ -28,38 +28,37 @@ literals=
literals=literals || q.k
end /*k*/
!=
/*▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ text parsing ▒▒▒▒▒▒▒▒*/
do j=1 to length(input); _=substr(input,j,1) /**/
/**/
if quoted then do; !=! || _ /**/
if _==literalStart then quoted=0 /**/
iterate /**/
end /**/
/**/
if pos(_,literals)\==0 then do; literalStart=_ /**/
!=! || _ /**/
quoted=1 /**/
iterate /**/
end /**/
/**/
if pos(_,atoms)==0 then do; !=! || _ ; iterate; end /**/
else do; call add!; !=_; end /**/
/**/
if pos(_,literals)==0 then do /**/
if pos(_,groupu)\==0 then level=level+1 /**/
call add! /**/
if pos(_,groupd)\==0 then level=level-1 /**/
if level<0 then say 'oops, mismatched' _ /**/
end /**/
end /*j*/ /**/
/**/
call add! /*handle any residual tokens.*/ /**/
if level\==0 then say 'oops, mismatched grouping symbol' /**/
if quoted then say 'oops, no end of quoted literal' literalStart /**/
/*▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒*/
/*▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ text parsing ▒▒▒▒▒▒▒▒*/
do j=1 to length(input); _=substr(input,j,1) /**/
/**/
if quoted then do; !=! || _ /**/
if _==literalStart then quoted=0 /**/
iterate /**/
end /**/
/**/
if pos(_,literals)\==0 then do; literalStart=_ /**/
!=! || _ /**/
quoted=1 /**/
iterate /**/
end /**/
/**/
if pos(_,atoms)==0 then do; !=! || _ ; iterate; end /**/
else do; call add!; !=_; end /**/
/**/
if pos(_,literals)==0 then do; if pos(_,groupu)\==0 then level=level+1 /**/
call add! /**/
if pos(_,groupd)\==0 then level=level-1 /**/
if level<0 then say 'oops, mismatched' _ /**/
end /**/
end /*j*/ /**/
/**/
call add! /*handle any residual tokens.*/ /**/
if level\==0 then say 'oops, mismatched grouping symbol' /**/
if quoted then say 'oops, no end of quoted literal' literalStart /**/
/*▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒*/
do j=1 for #; say $.j; end /*display the tokens to the terminal. */
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
add!: if !\='' then do; #=#+1; $.#=left('', max(0, tabs*(level-1)))!; end; !=
return
do m=1 for #; say $.m; end /*m*/ /*display the tokens to the terminal. */
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
add!: if !\='' then do; #=#+1; $.#=left('', max(0, tabs*(level-1)))!; end; !=
return

View file

@ -1,4 +1,44 @@
$ txr -c '@(do (print (read)) (put-line ""))'
((data "quoted data" 123 4.5) <- input from TTY
(data (!@# (4.5) "(more" "data)")))
((data "quoted data" 123 4.5) (data (! (sys:var #) (4.5) "(more" "data)"))) <- output
@(define float (f))@\
@(local (tok))@\
@(cases)@\
@{tok /[+\-]?\d+\.\d*([Ee][+\-]?\d+)?/}@\
@(or)@\
@{tok /[+\-]?\d*\.\d+([Ee][+\-]?\d+)?/}@\
@(or)@\
@{tok /[+\-]?\d+[Ee][+\-]?\d+/}@\
@(end)@\
@(bind f @(flo-str tok))@\
@(end)
@(define int (i))@\
@(local (tok))@\
@{tok /[+\-]?\d+/}@\
@(bind i @(int-str tok))@\
@(end)
@(define sym (s))@\
@(local (tok))@\
@{tok /[^\s()]+/}@\
@(bind s @(intern tok))@\
@(end)
@(define str (s))@\
@(local (tok))@\
@{tok /"(\\"|[^"])*"/}@\
@(bind s @[tok 1..-1])@\
@(end)
@(define atom (a))@\
@(cases)@\
@(float a)@(or)@(int a)@(or)@(str a)@(or)@(sym a)@\
@(end)@\
@(end)
@(define expr (e))@\
@(cases)@\
@/\s*/@(atom e)@\
@(or)@\
@/\s*\(\s*/@(coll :vars (e))@(expr e)@/\s*/@(last))@(end)@\
@(end)@\
@(end)
@(freeform)
@(expr e)@junk
@(output)
expr: @(format nil "~s" e)
junk: @junk
@(end)

View file

@ -1,44 +1 @@
@(define float (f))@\
@(local (tok))@\
@(cases)@\
@{tok /[+\-]?\d+\.\d*([Ee][+\-]?\d+)?/}@\
@(or)@\
@{tok /[+\-]?\d*\.\d+([Ee][+\-]?\d+)?/}@\
@(or)@\
@{tok /[+\-]?\d+[Ee][+\-]?\d+/}@\
@(end)@\
@(bind f @(flo-str tok))@\
@(end)
@(define int (i))@\
@(local (tok))@\
@{tok /[+\-]?\d+/}@\
@(bind i @(int-str tok))@\
@(end)
@(define sym (s))@\
@(local (tok))@\
@{tok /[^\s()]+/}@\
@(bind s @(intern tok))@\
@(end)
@(define str (s))@\
@(local (tok))@\
@{tok /"(\\"|[^"])*"/}@\
@(bind s @[tok 1..-1])@\
@(end)
@(define atom (a))@\
@(cases)@\
@(float a)@(or)@(int a)@(or)@(str a)@(or)@(sym a)@\
@(end)@\
@(end)
@(define expr (e))@\
@(cases)@\
@/\s*/@(atom e)@\
@(or)@\
@/\s*\(\s*/@(coll :vars (e))@(expr e)@/\s*/@(last))@(end)@\
@(end)@\
@(end)
@(freeform)
@(expr e)@junk
@(output)
expr: @(format nil "~s" e)
junk: @junk
@(end)
@/\s*\(\s*/@(coll :vars (e))@(expr e)@/\s*/@(last))@(end)