sicp icon indicating copy to clipboard operation
sicp copied to clipboard

Chapter 4: Exercise 4.2

Open EmilyOng opened this issue 1 year ago • 1 comments

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())"

EmilyOng avatar Aug 27 '23 08:08 EmilyOng