How to resolve the algorithm S-expressions step by step in the JavaScript programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm S-expressions step by step in the JavaScript programming language

Table of Contents

Problem Statement

S-Expressions   are one convenient way to parse and store data.

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. “()”   inside quoted strings are not interpreted, but treated as part of the string. Handling escaped quotes inside a string is optional;   thus “(foo"bar)” maybe treated as a string “foo"bar”, or as an error. For this, the reader need not recognize “\” for escaping, but should, in addition, recognize numbers if the language has appropriate datatypes. Languages that support it may treat unquoted strings as symbols. Note that with the exception of “()"” (“\” if escaping is supported) and whitespace there are no special characters. Anything else is allowed without quotes. The reader should be able to read the following input and turn it into a native datastructure. (see the Pike, Python and Ruby implementations for examples of native data structures.) 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.

Let the writer produce pretty printed output with indenting and line-breaks.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm S-expressions step by step in the JavaScript programming language

The code you provided is an implementation of a S-expression parser and serializer in JavaScript.

S-expressions (also known as sexprs) are a Lisp-like data structure that represents nested lists of symbols, strings, and numbers. They are often used to represent code or data in a structured manner.

Tokenization:

The first step in parsing an S-expression is to tokenize it. This involves splitting the input string into a series of tokens, which can be symbols (e.g., "data"), strings (e.g., ""quoted data""), or numbers (e.g., "123"). The tokenized function in the code you provided uses regular expressions to split the input string into tokens.

Parsing:

Once the input string has been tokenized, the next step is to parse it into an S-expression. The parseExpr function in the code you provided uses a recursive descent parser to parse the tokens into a nested list of S-expressions.

The parser starts at the beginning of the list of tokens and reads the first token. If the token is an open parenthesis, the parser recursively parses the tokens until it finds a closing parenthesis. The tokens between the parentheses are parsed into a nested list of S-expressions.

Serialization:

Once an S-expression has been parsed, it can be serialized back into a string. The serialized function in the code you provided converts an S-expression into a string representation.

The serializer starts at the top of the S-expression and serializes each element of the list recursively. If the element is a symbol, it is converted to a string without any quotes. If the element is a string, it is converted to a string with quotes around it. If the element is a number, it is converted to a string without any quotes.

Pretty Printing:

The toPretty function in the code you provided formats an S-expression so that it is easier to read. The function indents the elements of the S-expression and adds newlines between the elements.

Usage:

The main function in the code you provided parses an S-expression from a string and then serializes it back into a string. The serialized string is then output to the console.

Example:

The following code parses the S-expression ((data "quoted data" 123 4.5) (data (!@# (4.5) "(more" "data)"))) and serializes it back into a string:

const expr = '((data "quoted data" 123 4.5)\n (data (!@# (4.5) "(more" "data)")))'
const s = JSON.stringify(parseExpr(tokenized(expr)).
 [0], null, 2)
console.log(s)

The output of the code is:

"((data \"quoted data\" 123 4.5)\n (data (!@# (4.5) \"(more\" \"data)\")))"

Source code in the javascript programming language

String.prototype.parseSexpr = function() {
	var t = this.match(/\s*("[^"]*"|\(|\)|"|[^\s()"]+)/g)
	for (var o, c=0, i=t.length-1; i>=0; i--) {
		var n, ti = t[i].trim()
		if (ti == '"') return
		else if (ti == '(') t[i]='[', c+=1
		else if (ti == ')') t[i]=']', c-=1
		else if ((n=+ti) == ti) t[i]=n
		else t[i] = '\'' + ti.replace('\'', '\\\'') + '\''
		if (i>0 && ti!=']' && t[i-1].trim()!='(' ) t.splice(i,0, ',')
		if (!c) if (!o) o=true; else return
	}
	return c ? undefined : eval(t.join(''))
}

Array.prototype.toString = function() {
	var s=''; for (var i=0, e=this.length; i<e; i++) s+=(s?' ':'')+this[i]
	return '('+s+')'
}

Array.prototype.toPretty = function(s) {
	if (!s) s = ''
	var r = s + '(<br>'
	var s2 = s + Array(6).join('&nbsp;')
	for (var i=0, e=this.length; i<e; i+=1) { 
		var ai = this[i]
		r += ai.constructor != Array ? s2+ai+'<br>' : ai.toPretty(s2)
	}
	return r + s + ')<br>'
}

var str = '((data "quoted data" 123 4.5)\n (data (!@# (4.5) "(more" "data)")))'
document.write('text:<br>', str.replace(/\n/g,'<br>').replace(/ /g,'&nbsp;'), '<br><br>')
var sexpr = str.parseSexpr()
if (sexpr === undefined) 
	document.write('Invalid s-expr!', '<br>')
else 
	document.write('s-expr:<br>', sexpr, '<br><br>', sexpr.constructor != Array ? '' : 'pretty print:<br>' + sexpr.toPretty())


(() => {
    "use strict";

    // ------------------ S-EXPRESSIONS ------------------
    const main = () => {
        const expr = [
            "((data \"quoted data\" 123 4.5)",
            "  (data (!@# (4.5) \"(more\" \"data)\")))"
        ].join("\n");

        const [parse, residue] = parseExpr(
            tokenized(expr)
        );

        return 0 < residue.length ? (
            `Unparsed tokens: ${JSON.stringify(residue)}`
        ) : 0 < parse.length ? ([
            JSON.stringify(parse, null, 2),
            "Reserialized from parse:",
            parse.map(serialized).join(" ")
        ].join("\n\n")) : "Could not be parsed";
    };

    // ---------------- EXPRESSION PARSER ----------------

    // parseExpr [String] -> ([Expr], [String])
    const parseExpr = tokens =>
        // A tuple of (parsed trees, residual tokens)
        // derived from a list of tokens.
        until(finished)(readToken)([
            [], tokens
        ]);


    // finished :: ([Expr], [String]) -> Bool
    const finished = ([, tokens]) =>
        // True if no tokens remain, or the next
        // closes a sub-expression.
        0 === tokens.length || ")" === tokens[0];


    // readToken :: ([Expr], [String]) -> ([Expr], [String])
    const readToken = ([xs, tokens]) => {
        // A tuple of enriched expressions and
        // depleted tokens.
        const [token, ...ts] = tokens;

        // An open bracket introduces recursion over
        // a sub-expression to define a sub-list.
        return "(" === token ? (() => {
            const [expr, rest] = parseExpr(ts);

            return [xs.concat([expr]), rest.slice(1)];
        })() : ")" === token ? (
            [xs, token]
        ) : [xs.concat(atom(token)), ts];
    };

    // ------------------- ATOM PARSER -------------------

    // atom :: String -> Expr
    const atom = s =>
        0 < s.length ? (
            isNaN(s) ? (
                "\"'".includes(s[0]) ? (
                    s.slice(1, -1)
                ) : {
                    name: s
                }
            ) : parseFloat(s, 10)
        ) : "";


    // ------------------ TOKENIZATION -------------------

    // tokenized :: String -> [String]
    const tokenized = s =>
        // Brackets and quoted or unquoted atomic strings.
        quoteTokens("\"")(s).flatMap(
            segment => "\"" !== segment[0] ? (
                segment.replace(/([()])/gu, " $1 ")
                .split(/\s+/u)
                .filter(Boolean)
            ) : [segment]
        );


    // quoteTokens :: Char -> String -> [String]
    const quoteTokens = q =>
        // Alternating unquoted and quoted segments.
        s => s.split(q).flatMap(
            (k, i) => even(i) ? (
                Boolean(k) ? (
                    [k]
                ) : []
            ) : [`${q}${k}${q}`]
        );

    // ------------------ SERIALIZATION ------------------

    // serialized :: Expr -> String
    const serialized = e => {
        const t = typeof e;

        return "number" === t ? (
            `${e}`
        ) : "string" === t ? (
            `"${e}"`
        ) : "object" === t ? (
            Array.isArray(e) ? (
                `(${e.map(serialized).join(" ")})`
            ) : e.name
        ) : "?";
    };


    // --------------------- GENERIC ---------------------

    // even :: Int -> Bool
    const even = n =>
        // True if 2 is a factor of n.
        0 === n % 2;


    // until :: (a -> Bool) -> (a -> a) -> a -> a
    const until = p =>
        // The value resulting from repeated applications
        // of f to the seed value x, terminating when
        // that result returns true for the predicate p.
        f => {
            const go = x =>
                p(x) ? x : go(f(x));

            return go;
        };

    return main();
})();


  

You may also check:How to resolve the algorithm Sorting algorithms/Quicksort step by step in the Simula programming language
You may also check:How to resolve the algorithm Sort an integer array step by step in the J programming language
You may also check:How to resolve the algorithm Mian-Chowla sequence step by step in the Sidef programming language
You may also check:How to resolve the algorithm Loops/Infinite step by step in the Transact-SQL programming language
You may also check:How to resolve the algorithm Compound data type step by step in the Clojure programming language