syntax_tree-json 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +4 -0
- data/.github/workflows/auto-merge.yml +22 -0
- data/.github/workflows/main.yml +9 -16
- data/.gitmodules +3 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +4 -5
- data/Rakefile +7 -0
- data/lib/syntax_tree/json/ast.rb +184 -0
- data/lib/syntax_tree/json/format.rb +81 -0
- data/lib/syntax_tree/json/parser.rb +138 -0
- data/lib/syntax_tree/json/pretty_print.rb +71 -0
- data/lib/syntax_tree/json/version.rb +1 -1
- data/lib/syntax_tree/json/visitor.rb +44 -0
- data/lib/syntax_tree/json.rb +10 -109
- data/syntax_tree-json.gemspec +14 -15
- metadata +9 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c676b951d917802a8fde6f1f143a31f1709c32567cb8e9863012366da44ce4d5
|
4
|
+
data.tar.gz: f6341a94367cb2e1d071683a522cf2458fa37f3e56a9bc7fc9abfc38efa6c476
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c70f7298f8292e2b3759d33afb2028f3d550207cba7de7ac02b1fa6ab1f03f1a2914a8258e84f94710bd3ee6dbc4fa6ade42e67fda6e13476b1cbc1a9f527a0
|
7
|
+
data.tar.gz: d390116edbbdf9169be5d6734b7532e467eaaf684031fa74e8b5f7ad16dba03a438763fa1924e73fd9f4cd1202f6b7ec951607f79069ee1b3b96d7b13bb67e87
|
data/.github/dependabot.yml
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
name: Dependabot auto-merge
|
2
|
+
on: pull_request
|
3
|
+
|
4
|
+
permissions:
|
5
|
+
contents: write
|
6
|
+
pull-requests: write
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
dependabot:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
if: ${{ github.actor == 'dependabot[bot]' }}
|
12
|
+
steps:
|
13
|
+
- name: Dependabot metadata
|
14
|
+
id: metadata
|
15
|
+
uses: dependabot/fetch-metadata@v1.3.3
|
16
|
+
with:
|
17
|
+
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
18
|
+
- name: Enable auto-merge for Dependabot PRs
|
19
|
+
run: gh pr merge --auto --merge "$PR_URL"
|
20
|
+
env:
|
21
|
+
PR_URL: ${{github.event.pull_request.html_url}}
|
22
|
+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
data/.github/workflows/main.yml
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
name: Main
|
2
2
|
on:
|
3
3
|
- push
|
4
|
-
-
|
4
|
+
- pull_request
|
5
|
+
|
5
6
|
jobs:
|
6
7
|
ci:
|
7
8
|
name: CI
|
@@ -10,23 +11,15 @@ jobs:
|
|
10
11
|
CI: true
|
11
12
|
steps:
|
12
13
|
- uses: actions/checkout@master
|
14
|
+
with:
|
15
|
+
submodules: recursive
|
16
|
+
|
13
17
|
- uses: ruby/setup-ruby@v1
|
14
18
|
with:
|
15
19
|
bundler-cache: true
|
16
20
|
ruby-version: '3.1'
|
21
|
+
|
17
22
|
- name: Test
|
18
|
-
run:
|
19
|
-
|
20
|
-
|
21
|
-
needs: ci
|
22
|
-
runs-on: ubuntu-latest
|
23
|
-
if: github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]'
|
24
|
-
steps:
|
25
|
-
- uses: actions/github-script@v3
|
26
|
-
with:
|
27
|
-
script: |
|
28
|
-
github.pulls.merge({
|
29
|
-
owner: context.payload.repository.owner.login,
|
30
|
-
repo: context.payload.repository.name,
|
31
|
-
pull_number: context.payload.pull_request.number
|
32
|
-
})
|
23
|
+
run: |
|
24
|
+
bundle exec rake test
|
25
|
+
bundle exec rake stree:check
|
data/.gitmodules
ADDED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.3.0] - 2022-08-09
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- Dropped `json` as a dependency, doing all parsing in-gem.
|
14
|
+
|
9
15
|
## [0.2.0] - 2022-05-13
|
10
16
|
|
11
17
|
### Changed
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree-json (0.
|
5
|
-
json
|
4
|
+
syntax_tree-json (0.3.0)
|
6
5
|
prettier_print
|
7
6
|
syntax_tree (>= 2.0.1)
|
8
7
|
|
@@ -10,8 +9,7 @@ GEM
|
|
10
9
|
remote: https://rubygems.org/
|
11
10
|
specs:
|
12
11
|
docile (1.4.0)
|
13
|
-
|
14
|
-
minitest (5.15.0)
|
12
|
+
minitest (5.16.2)
|
15
13
|
prettier_print (0.1.0)
|
16
14
|
rake (13.0.6)
|
17
15
|
simplecov (0.21.2)
|
@@ -20,10 +18,11 @@ GEM
|
|
20
18
|
simplecov_json_formatter (~> 0.1)
|
21
19
|
simplecov-html (0.12.3)
|
22
20
|
simplecov_json_formatter (0.1.4)
|
23
|
-
syntax_tree (
|
21
|
+
syntax_tree (3.3.0)
|
24
22
|
prettier_print
|
25
23
|
|
26
24
|
PLATFORMS
|
25
|
+
arm64-darwin-21
|
27
26
|
x86_64-darwin-21
|
28
27
|
x86_64-linux
|
29
28
|
|
data/Rakefile
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "rake/testtask"
|
5
|
+
require "syntax_tree/rake_tasks"
|
5
6
|
|
6
7
|
Rake::TestTask.new(:test) do |t|
|
7
8
|
t.libs << "test"
|
@@ -9,4 +10,10 @@ Rake::TestTask.new(:test) do |t|
|
|
9
10
|
t.test_files = FileList["test/**/*_test.rb"]
|
10
11
|
end
|
11
12
|
|
13
|
+
SOURCE_FILES =
|
14
|
+
FileList[%w[Gemfile Rakefile syntax_tree-json.gemspec lib/**/*.rb test/*.rb]]
|
15
|
+
|
16
|
+
SyntaxTree::Rake::CheckTask.new { |t| t.source_files = SOURCE_FILES }
|
17
|
+
SyntaxTree::Rake::WriteTask.new { |t| t.source_files = SOURCE_FILES }
|
18
|
+
|
12
19
|
task default: :test
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module JSON
|
5
|
+
module AST
|
6
|
+
# This is the parent node of all of the nodes in the AST.
|
7
|
+
class Node
|
8
|
+
def format(q)
|
9
|
+
accept(Format.new(q))
|
10
|
+
end
|
11
|
+
|
12
|
+
def pretty_print(q)
|
13
|
+
accept(PrettyPrint.new(q))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# This represents an array in the tree.
|
18
|
+
class Array < Node
|
19
|
+
attr_reader :values
|
20
|
+
|
21
|
+
def initialize(values:)
|
22
|
+
@values = values
|
23
|
+
end
|
24
|
+
|
25
|
+
def accept(visitor)
|
26
|
+
visitor.visit_array(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def child_nodes
|
30
|
+
values
|
31
|
+
end
|
32
|
+
|
33
|
+
alias deconstruct child_nodes
|
34
|
+
|
35
|
+
def deconstruct_keys(keys)
|
36
|
+
{ values: values }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# This represents a false in the tree.
|
41
|
+
class False < Node
|
42
|
+
def accept(visitor)
|
43
|
+
visitor.visit_false(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def child_nodes
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
alias deconstruct child_nodes
|
51
|
+
|
52
|
+
def deconstruct_keys(keys)
|
53
|
+
{}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# This represents a null in the tree.
|
58
|
+
class Null < Node
|
59
|
+
def accept(visitor)
|
60
|
+
visitor.visit_null(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
def child_nodes
|
64
|
+
[]
|
65
|
+
end
|
66
|
+
|
67
|
+
alias deconstruct child_nodes
|
68
|
+
|
69
|
+
def deconstruct_keys(keys)
|
70
|
+
{}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# This represents a number in the tree.
|
75
|
+
class Number < Node
|
76
|
+
attr_reader :value
|
77
|
+
|
78
|
+
def initialize(value:)
|
79
|
+
@value = value
|
80
|
+
end
|
81
|
+
|
82
|
+
def accept(visitor)
|
83
|
+
visitor.visit_number(self)
|
84
|
+
end
|
85
|
+
|
86
|
+
def child_nodes
|
87
|
+
[]
|
88
|
+
end
|
89
|
+
|
90
|
+
alias deconstruct child_nodes
|
91
|
+
|
92
|
+
def deconstruct_keys(keys)
|
93
|
+
{ value: value }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# This represents an object in the tree.
|
98
|
+
class Object < Node
|
99
|
+
attr_reader :values
|
100
|
+
|
101
|
+
def initialize(values:)
|
102
|
+
@values = values
|
103
|
+
end
|
104
|
+
|
105
|
+
def accept(visitor)
|
106
|
+
visitor.visit_object(self)
|
107
|
+
end
|
108
|
+
|
109
|
+
def child_nodes
|
110
|
+
values.values
|
111
|
+
end
|
112
|
+
|
113
|
+
alias deconstruct child_nodes
|
114
|
+
|
115
|
+
def deconstruct_keys(keys)
|
116
|
+
{ values: values }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# This is the top of the JSON syntax tree.
|
121
|
+
class Root < Node
|
122
|
+
attr_reader :value
|
123
|
+
|
124
|
+
def initialize(value:)
|
125
|
+
@value = value
|
126
|
+
end
|
127
|
+
|
128
|
+
def accept(visitor)
|
129
|
+
visitor.visit_root(self)
|
130
|
+
end
|
131
|
+
|
132
|
+
def child_nodes
|
133
|
+
[value]
|
134
|
+
end
|
135
|
+
|
136
|
+
alias deconstruct child_nodes
|
137
|
+
|
138
|
+
def deconstruct_keys(keys)
|
139
|
+
{ value: value }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# This represents a string in the tree.
|
144
|
+
class String < Node
|
145
|
+
attr_reader :value
|
146
|
+
|
147
|
+
def initialize(value:)
|
148
|
+
@value = value
|
149
|
+
end
|
150
|
+
|
151
|
+
def accept(visitor)
|
152
|
+
visitor.visit_string(self)
|
153
|
+
end
|
154
|
+
|
155
|
+
def child_nodes
|
156
|
+
[]
|
157
|
+
end
|
158
|
+
|
159
|
+
alias deconstruct child_nodes
|
160
|
+
|
161
|
+
def deconstruct_keys(keys)
|
162
|
+
{ value: value }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# This represents a true in the tree.
|
167
|
+
class True < Node
|
168
|
+
def accept(visitor)
|
169
|
+
visitor.visit_true(self)
|
170
|
+
end
|
171
|
+
|
172
|
+
def child_nodes
|
173
|
+
[]
|
174
|
+
end
|
175
|
+
|
176
|
+
alias deconstruct child_nodes
|
177
|
+
|
178
|
+
def deconstruct_keys(keys)
|
179
|
+
{}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module JSON
|
5
|
+
# This class is a visitor responsible for formatting the AST.
|
6
|
+
class Format < Visitor
|
7
|
+
attr_reader :q
|
8
|
+
|
9
|
+
def initialize(q)
|
10
|
+
@q = q
|
11
|
+
end
|
12
|
+
|
13
|
+
# Visit an AST::Array node.
|
14
|
+
def visit_array(node)
|
15
|
+
q.group do
|
16
|
+
q.text("[")
|
17
|
+
|
18
|
+
q.indent do
|
19
|
+
q.breakable("")
|
20
|
+
q.seplist(node.values) { |value| visit(value) }
|
21
|
+
end
|
22
|
+
|
23
|
+
q.breakable("")
|
24
|
+
q.text("]")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Visit an AST::False node.
|
29
|
+
def visit_false(node)
|
30
|
+
q.text("false")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Visit an AST::Null node.
|
34
|
+
def visit_null(node)
|
35
|
+
q.text("null")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Visit an AST::Number node.
|
39
|
+
def visit_number(node)
|
40
|
+
q.text(node.value)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Visit an AST::Object node.
|
44
|
+
def visit_object(node)
|
45
|
+
q.group do
|
46
|
+
q.text("{")
|
47
|
+
|
48
|
+
q.indent do
|
49
|
+
q.breakable
|
50
|
+
q.seplist(node.values, nil, :each_pair) do |key, value|
|
51
|
+
q.group do
|
52
|
+
q.text(key)
|
53
|
+
q.text(": ")
|
54
|
+
visit(value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
q.breakable
|
60
|
+
q.text("}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Visit an AST::Root node.
|
65
|
+
def visit_root(node)
|
66
|
+
visit(node.value)
|
67
|
+
q.breakable(force: true)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Visit an AST::String node.
|
71
|
+
def visit_string(node)
|
72
|
+
q.text(node.value)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Visit an AST::True node.
|
76
|
+
def visit_true(node)
|
77
|
+
q.text("true")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module JSON
|
5
|
+
# This class is responsible for converting from a plain string input into
|
6
|
+
# an AST.
|
7
|
+
class Parser
|
8
|
+
class ParseError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :source
|
12
|
+
|
13
|
+
def initialize(source)
|
14
|
+
@source = source
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse
|
18
|
+
if parse_item(make_tokens) in [value, []]
|
19
|
+
AST::Root.new(value: value)
|
20
|
+
else
|
21
|
+
raise ParseError, "unexpected tokens after value"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# This represents a parsed token from the source.
|
28
|
+
class Token
|
29
|
+
attr_reader :type, :value
|
30
|
+
|
31
|
+
def initialize(type:, value: nil)
|
32
|
+
@type = type
|
33
|
+
@value = value
|
34
|
+
end
|
35
|
+
|
36
|
+
def deconstruct_keys(keys)
|
37
|
+
{ type: type, value: value }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def make_tokens
|
42
|
+
buffer = source.dup.force_encoding("UTF-8")
|
43
|
+
raise ParseError, "invalid UTF-8" unless buffer.valid_encoding?
|
44
|
+
|
45
|
+
tokens = []
|
46
|
+
buffer.gsub!(/\A\s+/, "")
|
47
|
+
|
48
|
+
until buffer.empty?
|
49
|
+
tokens << case buffer
|
50
|
+
in /\A[\{\}\[\],:]/
|
51
|
+
Token.new(type: $&.to_sym)
|
52
|
+
in /\A-?(0|[1-9]\d*)(\.\d+)?([Ee][-+]?\d+)?/
|
53
|
+
Token.new(type: :number, value: $&)
|
54
|
+
in %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"}
|
55
|
+
Token.new(type: :string, value: $&)
|
56
|
+
in /\Atrue/
|
57
|
+
Token.new(type: :true)
|
58
|
+
in /\Afalse/
|
59
|
+
Token.new(type: :false)
|
60
|
+
in /\Anull/
|
61
|
+
Token.new(type: :null)
|
62
|
+
else
|
63
|
+
raise ParseError, "unexpected token: #{buffer[0]}"
|
64
|
+
end
|
65
|
+
|
66
|
+
buffer = $'.gsub(/\A\s+/, "")
|
67
|
+
end
|
68
|
+
|
69
|
+
tokens
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_array(tokens)
|
73
|
+
values = []
|
74
|
+
|
75
|
+
loop do
|
76
|
+
value, tokens = parse_item(tokens)
|
77
|
+
values << value
|
78
|
+
|
79
|
+
case tokens
|
80
|
+
in [Token[type: :"]"], *rest]
|
81
|
+
return AST::Array.new(values: values), rest
|
82
|
+
in [Token[type: :","], *rest]
|
83
|
+
tokens = rest
|
84
|
+
else
|
85
|
+
raise ParseError, "expected ',' or ']' after array value"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_object(tokens)
|
91
|
+
values = {}
|
92
|
+
|
93
|
+
loop do
|
94
|
+
if tokens in [{ type: :string, value: key }, { type: :":" }, *tokens]
|
95
|
+
value, tokens = parse_item(tokens)
|
96
|
+
values[key] = value
|
97
|
+
|
98
|
+
case tokens
|
99
|
+
in [{ type: :"}" }, *rest]
|
100
|
+
return AST::Object.new(values: values), rest
|
101
|
+
in [{ type: :"," }, *rest]
|
102
|
+
tokens = rest
|
103
|
+
else
|
104
|
+
raise ParseError, "expected ',' or '}' after object value"
|
105
|
+
end
|
106
|
+
else
|
107
|
+
raise ParseError, "expected key and ':' after opening '{'"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_item(tokens)
|
113
|
+
case tokens
|
114
|
+
in [{ type: :"[" }, { type: :"]" }, *rest]
|
115
|
+
[AST::Array.new(values: []), rest]
|
116
|
+
in [{ type: :"[" }, *rest]
|
117
|
+
parse_array(rest)
|
118
|
+
in [{ type: :"{" }, { type: :"}" }, *rest]
|
119
|
+
[AST::Object.new(values: {}), rest]
|
120
|
+
in [{ type: :"{" }, *rest]
|
121
|
+
parse_object(rest)
|
122
|
+
in [{ type: :false }, *rest]
|
123
|
+
[AST::False.new, rest]
|
124
|
+
in [{ type: :true }, *rest]
|
125
|
+
[AST::True.new, rest]
|
126
|
+
in [{ type: :null }, *rest]
|
127
|
+
[AST::Null.new, rest]
|
128
|
+
in [{ type: :string, value: value }, *rest]
|
129
|
+
[AST::String.new(value: value), rest]
|
130
|
+
in [{ type: :number, value: }, *rest]
|
131
|
+
[AST::Number.new(value: value), rest]
|
132
|
+
else
|
133
|
+
raise ParseError, "unexpected token: #{tokens.first&.type}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module JSON
|
5
|
+
# This class is a visitor responsible for pretty-printing the AST.
|
6
|
+
class PrettyPrint < Visitor
|
7
|
+
attr_reader :q
|
8
|
+
|
9
|
+
def initialize(q)
|
10
|
+
@q = q
|
11
|
+
end
|
12
|
+
|
13
|
+
# Visit an AST::Array node.
|
14
|
+
def visit_array(node)
|
15
|
+
group("array") { field("values", node.values) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Visit an AST::False node.
|
19
|
+
def visit_false(node)
|
20
|
+
q.text("false")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Visit an AST::Null node.
|
24
|
+
def visit_null(node)
|
25
|
+
q.text("null")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Visit an AST::Number node.
|
29
|
+
def visit_number(node)
|
30
|
+
group("number") { field("value", node.value) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Visit an AST::Object node.
|
34
|
+
def visit_object(node)
|
35
|
+
group("object") { field("values", node.values) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Visit an AST::Root node.
|
39
|
+
def visit_root(node)
|
40
|
+
group("root") { field("value", node.value) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Visit an AST::String node.
|
44
|
+
def visit_string(node)
|
45
|
+
group("string") { field("value", node.value) }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Visit an AST::True node.
|
49
|
+
def visit_true(node)
|
50
|
+
q.text("true")
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def field(name, value)
|
56
|
+
q.breakable
|
57
|
+
q.text("#{name}=")
|
58
|
+
q.pp(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def group(name)
|
62
|
+
q.group do
|
63
|
+
q.text("(#{name}")
|
64
|
+
q.nest(2) { yield }
|
65
|
+
q.breakable("")
|
66
|
+
q.text(")")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module JSON
|
5
|
+
# This is the parent class of any visitors for this AST.
|
6
|
+
class Visitor
|
7
|
+
def visit(node)
|
8
|
+
node&.accept(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_all(nodes)
|
12
|
+
nodes.map { |node| visit(node) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_child_nodes(node)
|
16
|
+
visit_all(node.child_nodes)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Visit an AST::Array node.
|
20
|
+
alias visit_array visit_child_nodes
|
21
|
+
|
22
|
+
# Visit an AST::False node.
|
23
|
+
alias visit_false visit_child_nodes
|
24
|
+
|
25
|
+
# Visit an AST::Null node.
|
26
|
+
alias visit_null visit_child_nodes
|
27
|
+
|
28
|
+
# Visit an AST::Number node.
|
29
|
+
alias visit_number visit_child_nodes
|
30
|
+
|
31
|
+
# Visit an AST::Object node.
|
32
|
+
alias visit_object visit_child_nodes
|
33
|
+
|
34
|
+
# Visit an AST::Root node.
|
35
|
+
alias visit_root visit_child_nodes
|
36
|
+
|
37
|
+
# Visit an AST::String node.
|
38
|
+
alias visit_string visit_child_nodes
|
39
|
+
|
40
|
+
# Visit an AST::True node.
|
41
|
+
alias visit_true visit_child_nodes
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/syntax_tree/json.rb
CHANGED
@@ -1,131 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "json"
|
4
3
|
require "prettier_print"
|
5
4
|
require "syntax_tree"
|
6
5
|
|
6
|
+
require_relative "json/ast"
|
7
|
+
require_relative "json/parser"
|
7
8
|
require_relative "json/version"
|
9
|
+
require_relative "json/visitor"
|
10
|
+
|
11
|
+
require_relative "json/format"
|
12
|
+
require_relative "json/pretty_print"
|
8
13
|
|
9
14
|
module SyntaxTree
|
10
15
|
module JSON
|
11
|
-
# This is always the root of the syntax tree.
|
12
|
-
class RootNode < Struct.new(:object)
|
13
|
-
def format(q)
|
14
|
-
object.format(q)
|
15
|
-
q.breakable(force: true)
|
16
|
-
end
|
17
|
-
|
18
|
-
def pretty_print(q)
|
19
|
-
q.group(2, "(root", ")") do
|
20
|
-
q.breakable
|
21
|
-
q.text("object=")
|
22
|
-
q.pp(object)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# This contains an object node. The values argument to the struct is a hash.
|
28
|
-
class ObjectNode < Struct.new(:values)
|
29
|
-
def format(q)
|
30
|
-
q.group do
|
31
|
-
q.text("{")
|
32
|
-
|
33
|
-
q.indent do
|
34
|
-
q.breakable
|
35
|
-
q.seplist(values, nil, :each_pair) do |key, value|
|
36
|
-
q.group do
|
37
|
-
q.text(key.to_json)
|
38
|
-
q.text(": ")
|
39
|
-
value.format(q)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
q.breakable
|
45
|
-
q.text("}")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def pretty_print(q)
|
50
|
-
q.group(2, "(object", ")") do
|
51
|
-
q.breakable
|
52
|
-
q.text("values=")
|
53
|
-
q.pp(values)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# This contains an array node. The values argument to the struct is an
|
59
|
-
# array.
|
60
|
-
class ArrayNode < Struct.new(:values)
|
61
|
-
def format(q)
|
62
|
-
q.group do
|
63
|
-
q.text("[")
|
64
|
-
|
65
|
-
q.indent do
|
66
|
-
q.breakable("")
|
67
|
-
q.seplist(values) { |value| value.format(q) }
|
68
|
-
end
|
69
|
-
|
70
|
-
q.breakable("")
|
71
|
-
q.text("]")
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def pretty_print(q)
|
76
|
-
q.group(2, "(array", ")") do
|
77
|
-
q.breakable
|
78
|
-
q.text("values=")
|
79
|
-
q.pp(values)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# This contains a literal node. The value argument to the struct is a
|
85
|
-
# literal value like a string, number, or boolean.
|
86
|
-
class LiteralNode < Struct.new(:value)
|
87
|
-
def format(q)
|
88
|
-
q.text(value.to_json)
|
89
|
-
end
|
90
|
-
|
91
|
-
def pretty_print(q)
|
92
|
-
q.group(2, "(literal", ")") do
|
93
|
-
q.breakable
|
94
|
-
q.text("value=")
|
95
|
-
q.text(value.inspect)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
16
|
class << self
|
101
17
|
def format(source, maxwidth = 80)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
formatter.flush
|
106
|
-
formatter.output.join
|
18
|
+
PrettierPrint.format(+"", maxwidth) do |q|
|
19
|
+
parse(source).accept(Format.new(q))
|
20
|
+
end
|
107
21
|
end
|
108
22
|
|
109
23
|
def parse(source)
|
110
|
-
|
24
|
+
Parser.new(source).parse
|
111
25
|
end
|
112
26
|
|
113
27
|
def read(filepath)
|
114
28
|
File.read(filepath)
|
115
29
|
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
def translate(object)
|
120
|
-
case object
|
121
|
-
when Hash
|
122
|
-
ObjectNode.new(object.to_h { |key, value| [key, translate(value)] })
|
123
|
-
when Array
|
124
|
-
ArrayNode.new(object.map { |value| translate(value) })
|
125
|
-
else
|
126
|
-
LiteralNode.new(object)
|
127
|
-
end
|
128
|
-
end
|
129
30
|
end
|
130
31
|
end
|
131
32
|
|
data/syntax_tree-json.gemspec
CHANGED
@@ -3,27 +3,26 @@
|
|
3
3
|
require_relative "lib/syntax_tree/json/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name
|
7
|
-
spec.version
|
8
|
-
spec.authors
|
9
|
-
spec.email
|
6
|
+
spec.name = "syntax_tree-json"
|
7
|
+
spec.version = SyntaxTree::JSON::VERSION
|
8
|
+
spec.authors = ["Kevin Newton"]
|
9
|
+
spec.email = ["kddnewton@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary
|
12
|
-
spec.homepage
|
13
|
-
spec.license
|
14
|
-
spec.metadata
|
11
|
+
spec.summary = "Syntax Tree support for JSON"
|
12
|
+
spec.homepage = "https://github.com/ruby-syntax-tree/syntax_tree-json"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.metadata = { "rubygems_mfa_required" => "true" }
|
15
15
|
|
16
|
-
spec.files
|
17
|
-
|
18
|
-
|
16
|
+
spec.files =
|
17
|
+
Dir.chdir(__dir__) do
|
18
|
+
`git ls-files -z`.split("\x0")
|
19
|
+
.reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
20
|
end
|
20
|
-
end
|
21
21
|
|
22
|
-
spec.bindir
|
23
|
-
spec.executables
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = %w[lib]
|
25
25
|
|
26
|
-
spec.add_dependency "json"
|
27
26
|
spec.add_dependency "prettier_print"
|
28
27
|
spec.add_dependency "syntax_tree", ">= 2.0.1"
|
29
28
|
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syntax_tree-json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: json
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: prettier_print
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,8 +102,10 @@ extensions: []
|
|
116
102
|
extra_rdoc_files: []
|
117
103
|
files:
|
118
104
|
- ".github/dependabot.yml"
|
105
|
+
- ".github/workflows/auto-merge.yml"
|
119
106
|
- ".github/workflows/main.yml"
|
120
107
|
- ".gitignore"
|
108
|
+
- ".gitmodules"
|
121
109
|
- CHANGELOG.md
|
122
110
|
- Gemfile
|
123
111
|
- Gemfile.lock
|
@@ -127,7 +115,12 @@ files:
|
|
127
115
|
- bin/console
|
128
116
|
- bin/setup
|
129
117
|
- lib/syntax_tree/json.rb
|
118
|
+
- lib/syntax_tree/json/ast.rb
|
119
|
+
- lib/syntax_tree/json/format.rb
|
120
|
+
- lib/syntax_tree/json/parser.rb
|
121
|
+
- lib/syntax_tree/json/pretty_print.rb
|
130
122
|
- lib/syntax_tree/json/version.rb
|
123
|
+
- lib/syntax_tree/json/visitor.rb
|
131
124
|
- syntax_tree-json.gemspec
|
132
125
|
homepage: https://github.com/ruby-syntax-tree/syntax_tree-json
|
133
126
|
licenses:
|