sicp
sicp copied to clipboard
Chapter 4: Exercise 4.2
Here is my suggestion for Exercise 4.2: https://share.sourceacademy.org/c53du
Specifications on syntax predicates and selectors are taken from: https://sourceacademy.org/sicpjs/4.1.2
Supports
- Literals
- Names
- Applications
- Conditionals
- Lambdas
- Sequences
- Blocks
- Returns
- Assignments
- Declarations
- Functions
- Operators
Notes:
- Always surround the strings that result from unparsing operator combinations with parentheses to enforce respect for operator precedence
Code
function is_tagged_list(component, the_tag) {
return is_pair(component) && head(component) === the_tag;
}
// Literals
function is_literal(component) {
return is_tagged_list(component, "literal");
}
function literal_value(component) {
return head(tail(component));
}
function construct_literal(component) {
const literal = literal_value(component);
return is_string(literal)
? literal
: stringify(literal);
}
// Names
function is_name(component) {
return is_tagged_list(component, "name");
}
function symbol_of_name(component) {
return head(tail(component));
}
// Applications
function is_application(component) {
return is_tagged_list(component, "application");
}
function function_expression(component) {
return head(tail(component));
}
function arg_expressions(component) {
return head(tail(tail(component)));
}
function construct_function_application(component, unparse) {
// Constructs fun-expr(arg-expr_1, ..., arg-expr_n)
return unparse(function_expression(component)) +
bracketize(join(map(unparse, arg_expressions(component)), " , "));
}
// Conditionals
function is_conditional_expression(component) {
return is_tagged_list(component, "conditional_expression");
}
function is_conditional_statement(component) {
return is_tagged_list(component, "conditional_statement");
}
function conditional_predicate(component) {
return head(tail(component));
}
function conditional_consequent(component) {
return head(tail(tail(component)));
}
function conditional_alternative(component) {
return head(tail(tail(tail(component))));
}
function construct_conditional_expression(component, unparse) {
return unparse(conditional_predicate(component)) +
" ? " + unparse(conditional_consequent(component)) +
" : " + unparse(conditional_alternative(component));
}
function construct_conditional_statement(component, unparse) {
return "if " + bracketize(unparse(conditional_predicate(component))) +
" {\n" + unparse(conditional_consequent(component)) + "\n} " +
"else {\n" + unparse(conditional_alternative(component)) + "\n}";
}
// Lambdas
function is_lambda_expression(component) {
return is_tagged_list(component, "lambda_expression");
}
function lambda_parameter_symbols(component) {
return head(tail(component));
}
function lambda_body(component) {
return head(tail(tail(component)));
}
function construct_lambda_expression(component, unparse) {
const names = map(unparse, lambda_parameter_symbols(component));
const block = unparse(lambda_body(component));
return bracketize(join(names, " , ")) + " => " + block;
}
// Sequences
function is_sequence(component) {
return is_tagged_list(component, "sequence");
}
function sequence_statements(component) {
return head(tail(component));
}
function construct_sequence(component, unparse) {
return join(map(unparse, sequence_statements(component)), ";\n");
}
// Blocks
function is_block(component) {
return is_tagged_list(component, "block");
}
function block_body(component) {
return head(tail(component));
}
function construct_block(component, unparse) {
return "{\n" + unparse(block_body(component)) + "\n}";
}
// Returns
function is_return_statement(component) {
return is_tagged_list(component, "return_statement");
}
function return_expression(component) {
return head(tail(component));
}
function construct_return_statement(component, unparse) {
return "return " + unparse(return_expression(component));
}
// Assignments
function is_assignment(component) {
return is_tagged_list(component, "assignment");
}
function assignment_symbol(component) {
return head(tail(component));
}
function assignment_value_expression(component) {
return head(tail(tail(component)));
}
function construct_assignment(component, unparse) {
return unparse(assignment_symbol(component)) +
" = " +
unparse(assignment_value_expression(component));
}
// Declarations
function is_constant_declaratino(component) {
return is_tagged_list(component, "constant_declaration");
}
function is_variable_declaration(component) {
return is_tagged_list(component, "variable_declaration");
}
function declaration_symbol(component) {
return head(tail(component));
}
function declaration_value_expression(component) {
return head(tail(tail(component)));
}
function construct_constant_declaration(component, unparse) {
return "const " +
unparse(declaration_symbol(component)) + " = " +
unparse(declaration_value_expression(component));
}
function construct_variable_declaration(component, unparse) {
return "let " +
unparse(declaration_symbol(component)) + " = " +
unparse(declaration_value_expression(component));
}
// Functions
function is_function_declaration(component) {
return is_tagged_list(component, "function_declaration");
}
function function_declaration_name(component) {
return head(tail(component));
}
function function_declaration_parameters(component) {
return head(tail(tail(component)));
}
function function_declaration_body(component) {
return head(tail(tail(tail(component))));
}
function construct_function_declaration(component, unparse) {
return "function " +
unparse(function_declaration_name(component)) +
bracketize(join(map(unparse, function_declaration_parameters(component)), " , ")) +
" {\n" + unparse(function_declaration_body(component)) + "\n}";
}
// Operators
function is_unary_operator_combination(component) {
return is_tagged_list(component, "unary_operator_combination");
}
function is_binary_operator_combination(component) {
return is_tagged_list(component, "binary_operator_combination");
}
function operator_symbol(component) {
return head(tail(component));
}
function first_operand(component) {
return head(tail(tail(component)));
}
function second_operand(component) {
return head(tail(tail(tail(component))));
}
function construct_unary_operator_combination(component, unparse) {
// Always use parantheses to enforce respect for operator precedence
return operator_symbol(component) +
bracketize(unparse(first_operand(component)));
}
function construct_binary_operator_combination(component, unparse) {
// Always use parantheses to enforce respect for operator precedence
return bracketize(unparse(first_operand(component))) + " " +
operator_symbol(component) + " " +
bracketize(unparse(second_operand(component)));
}
// Helpers
function join(sequence, delimitter) {
return is_null(sequence)
? ""
: is_null(tail(sequence))
? head(sequence)
: head(sequence) + delimitter + join(tail(sequence), delimitter);
}
function bracketize(unparsed) {
return "(" + unparsed + ")";
}
function unparse(component) {
return is_literal(component)
? construct_literal(component)
: is_name(component)
? symbol_of_name(component)
: is_application(component)
? construct_function_application(component, unparse)
: is_conditional_expression(component)
? construct_conditional_expression(component, unparse)
: is_conditional_statement(component)
? construct_conditional_statement(component, unparse)
: is_lambda_expression(component)
? construct_lambda_expression(component, unparse)
: is_sequence(component)
? construct_sequence(component, unparse)
: is_block(component)
? construct_block(component, unparse)
: is_return_statement(component)
? construct_return_statement(component, unparse)
: is_assignment(component)
? construct_assignment(component, unparse)
: is_constant_declaratino(component)
? construct_constant_declaration(component, unparse)
: is_variable_declaration(component)
? construct_variable_declaration(component, unparse)
: is_function_declaration(component)
? construct_function_declaration(component, unparse)
: is_unary_operator_combination(component)
? construct_unary_operator_combination(component, unparse)
: is_binary_operator_combination(component)
? construct_binary_operator_combination(component, unparse)
: error(component, "unknown syntax -- unparse");
}
Some sample cases:
// Functions, returns, operators, conditionals, applications
display(unparse(parse("function factorial(n) { return n === 0 ? 1 : n * factorial(n - 1);}")));
// Lambdas, declarations, operators, conditionals, applications
display(unparse(parse("const factorial = n => n === 0 ? 1 : n * factorial(n - 1);")));
// Declarations, operators, applications, sequences
display(unparse(parse("const size = 2; let five_times = 5 * size; !true; 4 + p(5);")));
// Conditionals, applications
display(unparse(parse("if (is_even(n)) { display(\"even!\"); } else { display(\"odd!\"); } ")));
// Functions, conditionals, returns, operators, applications (this is the example for normal-order evaluation)
display(unparse(parse("function p() { return p(); } function f(x, y) { return x === 0 ? x : y; } f(0, p());")));
Result:
"function factorial(n) {\nreturn (n) === (0) ? 1 : (n) * (factorial((n) - (1)))\n}"
"const factorial = (n) => return (n) === (0) ? 1 : (n) * (factorial((n) - (1)))"
"const size = 2;\nlet five_times = (5) * (size);\n!(true);\n(4) + (p(5))"
"if (is_even(n)) {\ndisplay(even!)\n} else {\ndisplay(odd!)\n}"
"function p() {\nreturn p()\n};\nfunction f(x , y) {\nreturn (x) === (0) ? x : y\n};\nf(0 , p())"