How to Parse S-expressions in JavaScript?
Last Updated :
30 Jul, 2024
The most simple and powerful notation used in Lisp programming is S-expressions and they are composed of atoms, say numbers or symbols or strings and lists (which too are S-expressions) parsing S-expressions in JavaScript can be done using a few basic techniques.
What are S-expressions?
To represent nested list data structures, we use s-expressions, it has extensive applications in Lisp as well as other functional programming languages, an s-expression is either an atom or a list of s-expressions.
Examples:
Atom: 42, hello, +
List: (1 2 3), (define (square x) (* x x))
Approach
To implement an S-expression parser several things have to be done:
- Breaks the input string into tokens.
- Builds a tree out of these tokens representing the s-expression.
Steps to Parse S-expressions in JavaScript
Step 1: Tokenizing the Input
In the first step, you have to break the input string into tokens.
function tokenize(input) {
return input
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.trim()
.split(/\s+/);
}
Step 2: Parsing the Tokens
A function is going to be written by us, that will take a list of tokens and parse them into a tree structure.
function parse(tokens) {
if (tokens.length === 0) {
throw new SyntaxError("Unexpected EOF");
}
let token = tokens.shift();
if (token === '(') {
let list = [];
while (tokens[0] !== ')') {
list.push(parse(tokens));
}
tokens.shift(); // remove ')'
return list;
} else if (token === ')') {
throw new SyntaxError("Unexpected )");
} else {
return atom(token);
}
}
function atom(token) {
if (!isNaN(token)) {
return Number(token);
} else {
return token;
}
}
Step 3: Using the Parser
Having established our tokenizer and parser functions and we are now in a position to employ them in parsing an S-expression.
function parseSExpression(input) {
const tokens = tokenize(input);
return parse(tokens);
}
// Test the parser
const input = "(define (square x) (* x x))";
const parsed = parseSExpression(input);
console.log(JSON.stringify(parsed, null, 2));
Example 1: In the given below example we have a square function that takes one argument x in order to return its value squared, a simple function definition in Lisp like fashion is indicated by S-expression here.
JavaScript
function tokenize(input) {
return input
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.trim()
.split(/\s+/);
}
function parse(tokens) {
if (tokens.length === 0) {
throw new SyntaxError("Unexpected EOF");
}
let token = tokens.shift();
if (token === '(') {
let list = [];
while (tokens[0] !== ')') {
list.push(parse(tokens));
}
tokens.shift(); // remove ')'
return list;
} else if (token === ')') {
throw new SyntaxError("Unexpected )");
} else {
return atom(token);
}
}
function atom(token) {
if (!isNaN(token)) {
return Number(token);
} else {
return token;
}
}
function parseSExpression(input) {
const tokens = tokenize(input);
return parse(tokens);
}
// Example 1
const input1 = "(define (square x) (* x x))";
const parsed1 = parseSExpression(input1);
console.log("Example 1 - Input S-expression:", input1);
console.log("Example 1 - Parsed S-expression:", JSON.stringify(parsed1, null, 2));
OutputExample 1 - Input S-expression: (define (square x) (* x x))
Example 1 - Parsed S-expression: [
"define",
[
"square",
"x"
],
[
"*",
"x",
"x"
]
]
Example 2: For instance, we are dealing with the parsing of a conditional expression, basically, S-expression represents if statement that checks if x > 0, when true it adds 1 to x else it subtracts 1 from x.
JavaScript
function tokenize(input) {
return input
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.trim()
.split(/\s+/);
}
function parse(tokens) {
if (tokens.length === 0) {
throw new SyntaxError("Unexpected EOF");
}
let token = tokens.shift();
if (token === '(') {
let list = [];
while (tokens[0] !== ')') {
list.push(parse(tokens));
}
tokens.shift(); // remove ')'
return list;
} else if (token === ')') {
throw new SyntaxError("Unexpected )");
} else {
return atom(token);
}
}
function atom(token) {
if (!isNaN(token)) {
return Number(token);
} else {
return token;
}
}
function parseSExpression(input) {
const tokens = tokenize(input);
return parse(tokens);
}
// Example 2
const input3 = "(if (> x 0) (+ x 1) (- x 1))";
const parsed3 = parseSExpression(input3);
console.log("Example 2 - Input S-expression:", input3);
console.log("Example 2 - Parsed S-expression:", JSON.stringify(parsed3, null, 2));
OutputExample 2 - Input S-expression: (if (> x 0) (+ x 1) (- x 1))
Example 2 - Parsed S-expression: [
"if",
[
">",
"x",
0
],
[
"+",
"x",
1
],
[
"-",
"x",
1
...
Conclusion
In conclusion if you want to parse S-expressions in JavaScript you have to tokenize the input string then recursively parse tokens into a tree structure, this resultant structure can be used for other processing like interpreting or evaluating the S-expression and this parser handles basic S-expressions but can be modified to accommodate more complex syntaxes and features as required.
Similar Reads
How to Parse XML in JavaScript?
Parsing XML data is important because it allows JavaScript applications to extract structured information from XML documents. We will explore two different approaches to Parse XML in JavaScript. Below are the approaches to parsing XML in JavaScript: Table of Content Using DOM ParserUsing xml2js Libr
2 min read
How useful is learning regular expressions in JavaScript ?
Regular expressions (RegExp) are an important skill for JavaScript developers that helps in text manipulation, validation, and extraction. Whether youâre working on form validations, file handling, or log parsing, mastering regular expressions can simplify your code and improve productivity. Why Lea
3 min read
How to Parse JSON in JavaScript ?
Parse JSON in JavaScript, accepting a JSON string as input and returning a corresponding JavaScript object with two methods, using JSON.parse() for parsing JSON strings directly and employing the fetch API to parse JSON responses from web APIs. These techniques are crucial for seamless data manipula
2 min read
How to clone a given regular expression in JavaScript ?
In this article, we will know How to clone a regular expression using JavaScript. We can clone a given regular expression using the constructor RegExp(). The syntax of using this constructor has been defined as follows:- Syntax: new RegExp(regExp , flags) Here regExp is the expression to be cloned a
2 min read
JavaScript RegExp (x|y) Expression
The (x|y) expression in JavaScript regular expressions is used to match either x or y. It acts as an OR operator in regular expressions, allowing you to specify multiple patterns to match. [GFGTABS] JavaScript let regex = /(cat|dog)/g; let str = "I have a cat and a dog."; let matches = str
2 min read
How to Parse JSON Data in JavaScript?
To parse JSON data in JavaScript, you can use the JSON.parse() method. This method converts a JSON string into a JavaScript object, making it easier to work with the data. 1. Parse Simple JSON Strings[GFGTABS] JavaScript //Driver Code Starts{ const jsonS = '{"name": "Rahul",
2 min read
How to Access Matched Groups in a JavaScript Regular Expression ?
Here are the different methods to access matched groups in JavaScript regular Expression(RegExp). 1. Using exec() MethodThe exec() method returns an array with the entire match and captured groups, which you can access by their index in the result array. [GFGTABS] JavaScript let s = "The price
3 min read
How to use a Variable in Regular Expression in JavaScript ?
To dynamically create a regular expression (RegExp) in JavaScript using variables, you can use the RegExp constructor. Here are the various ways to use a variable in Regular Expression. 1. Using the RegExp Constructor with a VariableIn JavaScript, regular expressions can be created dynamically using
3 min read
JavaScript Function Expression
A function expression is a way to define a function as part of an expression making it versatile for assigning to variables, passing as arguments, or invoking immediately. Function expressions can be named or anonymous.They are not hoisted, meaning they are accessible only after their definition.Fre
3 min read
JavaScript function* expression
The function* is an inbuilt keyword in JavaScript which is used to define a generator function inside an expression. Syntax: function* [name]([param1[, param2[, ..., paramN]]]) { statements}Parameters: This function accepts the following parameter as mentioned above and described below: name: This p
2 min read