source-academy / sicp

XML sources of SICP and SICP JS, and support for generating Interactive SICP JS, PDF, e-book and comparison editions
https://sourceacademy.org/sicpjs
Creative Commons Attribution Share Alike 4.0 International
903 stars 119 forks source link

Chapter 4: Exercise 4.2 #914

Open EmilyOng opened 1 year ago

EmilyOng commented 1 year ago

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

Notes:

Code ```javascript 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())"
martin-henz commented 2 months ago

There were various issues with this solution, involving the placement of semicolons. Here is an improved version: https://share.sourceacademy.org/6yoyp The major remaining problem: expression statements that are applications do not end with a semicolon. To fix this, one would need to carefully distinguish between statements and expressions. So I'll mark this as "work-in-progress", hoping that someone finds the time for this.

Also: indentation would be nice!