Summary
Add set literal formatting support to the PTC-Lisp formatter, enabling {:set, [...]} AST nodes to be serialized as #{...} source code strings. This completes the set literal feature by enabling roundtrip verification (parse → format → parse) and debugging output for set expressions.
Context
Architecture reference: Set Literal Implementation Plan - Phase 7: Formatter
Dependencies: Phases 1-5 complete (parser, AST types, analyzer, evaluator, runtime)
Related issues: Part of epic #164
Current State
The formatter (lib/ptc_runner/lisp/formatter.ex) handles all AST node types except sets:
{:vector, elems} → "[#{format_list(elems)}]" (line 27-29)
{:map, pairs} → "{#{format_pairs(pairs)}}" (line 31-33)
- No
{:set, elems} clause exists
Attempting to format a set AST will raise a FunctionClauseError.
Acceptance Criteria
Implementation
Files to modify:
lib/ptc_runner/lisp/formatter.ex - Add format/1 clause for {:set, elems}
test/ptc_runner/lisp/formatter_test.exs - Add set formatting tests
Pattern to follow (line 27-29):
def format({:vector, elems}) do
"[#{format_list(elems)}]"
end
Implementation (add after map formatting, ~line 33):
def format({:set, elems}) do
"\#{#{format_list(elems)}}"
end
Critical escaping note:
Elixir's string interpolation uses #{} syntax. The set delimiter # must be escaped:
- Use
"\#{#{format_list(elems)}}" (escape the first #)
- The inner
#{...} is Elixir interpolation, the outer \# becomes literal #
Test Plan
Unit tests to add in test/ptc_runner/lisp/formatter_test.exs:
describe "sets" do
test "empty set" do
# Use string concat to avoid Elixir interpolation issues
assert Formatter.format({:set, []}) == "#" <> "{}"
end
test "set with elements" do
assert Formatter.format({:set, [1, 2, 3]}) == "#" <> "{1 2 3}"
end
test "set with mixed types" do
assert Formatter.format({:set, [1, {:keyword, :a}, {:symbol, :x}]}) ==
"#" <> "{1 :a x}"
end
test "nested set" do
assert Formatter.format({:set, [{:set, [1, 2]}]}) == "#" <> "{#" <> "{1 2}}"
end
end
Roundtrip tests:
describe "roundtrip verification" do
test "set roundtrip" do
set_str = "#" <> "{1 2 3}"
{:ok, ast} = Parser.parse(set_str)
formatted = Formatter.format(ast)
{:ok, reparsed} = Parser.parse(formatted)
assert ast == reparsed
end
test "nested set roundtrip" do
set_str = "#" <> "{#" <> "{1} [2 3]}"
{:ok, ast} = Parser.parse(set_str)
formatted = Formatter.format(ast)
{:ok, reparsed} = Parser.parse(formatted)
assert ast == reparsed
end
end
Note: Tests use string concatenation ("#" <> "{}") to avoid Elixir's #{} interpolation, following the same pattern used in parser_test.exs.
Out of Scope
- Pretty-printing with indentation (all collections are single-line)
- MapSet runtime value formatting (formatter handles AST, not evaluated values)
- Configurable output styles
Documentation Updates
None - the formatter is an internal module with no public API documentation.
Summary
Add set literal formatting support to the PTC-Lisp formatter, enabling
{:set, [...]}AST nodes to be serialized as#{...}source code strings. This completes the set literal feature by enabling roundtrip verification (parse → format → parse) and debugging output for set expressions.Context
Architecture reference: Set Literal Implementation Plan - Phase 7: Formatter
Dependencies: Phases 1-5 complete (parser, AST types, analyzer, evaluator, runtime)
Related issues: Part of epic #164
Current State
The formatter (
lib/ptc_runner/lisp/formatter.ex) handles all AST node types except sets:{:vector, elems}→"[#{format_list(elems)}]"(line 27-29){:map, pairs}→"{#{format_pairs(pairs)}}"(line 31-33){:set, elems}clause existsAttempting to format a set AST will raise a FunctionClauseError.
Acceptance Criteria
Formatter.format({:set, []})returns"#{}"(empty set)Formatter.format({:set, [1, 2, 3]})returns"#{1 2 3}"{:set, [{:set, [1]}]}→"#{#{1}}"Implementation
Files to modify:
lib/ptc_runner/lisp/formatter.ex- Addformat/1clause for{:set, elems}test/ptc_runner/lisp/formatter_test.exs- Add set formatting testsPattern to follow (line 27-29):
Implementation (add after map formatting, ~line 33):
Critical escaping note:
Elixir's string interpolation uses
#{}syntax. The set delimiter#must be escaped:"\#{#{format_list(elems)}}"(escape the first#)#{...}is Elixir interpolation, the outer\#becomes literal#Test Plan
Unit tests to add in
test/ptc_runner/lisp/formatter_test.exs:Roundtrip tests:
Note: Tests use string concatenation (
"#" <> "{}") to avoid Elixir's#{}interpolation, following the same pattern used inparser_test.exs.Out of Scope
Documentation Updates
None - the formatter is an internal module with no public API documentation.