asciidoctor-sail 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +7 -0
- data/README.adoc +7 -0
- data/lib/asciidoctor-sail/highlighter.rb +149 -0
- data/lib/asciidoctor-sail/macros.rb +279 -0
- data/lib/asciidoctor-sail/sources.rb +22 -0
- data/lib/asciidoctor-sail.rb +14 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3c02b2d48c65bd2e04e77fe09135f4ac55b41567e3ed6cfa8ca477ac650fe064
|
4
|
+
data.tar.gz: 5519b40e6be23cf4388d4ce15940f142ba3ee956f3027512ebd3d349d9a96816
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 217a7edc3c5b47ef72261cc21330949ec8b368863ae18857cf18f038c72e6ddcea6e6f27ff50c9c79809a8680d898840e99509063994fca58a069c500c741819
|
7
|
+
data.tar.gz: 8a9f7db2589280023e8e219b2db0ede1f546aa530c80b8bf3c8bddbe18e383ae7a6416b2e73d9f421540d81bb3fe59ebccd39ec02325d4e3535b5c34a093366b
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2023 Alasdair Armstrong
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.adoc
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
= Sail Asciidoctor Plugin
|
2
|
+
|
3
|
+
An https://asciidoctor.org[Asciidoctor] extension for including code from
|
4
|
+
https://github.com/rems-project/sail[Sail] ISA specifications into
|
5
|
+
Asciidoc documents, primarily aimed at supporting documentation for
|
6
|
+
RISC-V extensions using the
|
7
|
+
https://github.com/riscv/sail-riscv[sail-riscv] model.
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rouge'
|
4
|
+
|
5
|
+
# We define a rouge lexer for Sail source
|
6
|
+
class SailLexer < Rouge::RegexLexer
|
7
|
+
title 'Sail'
|
8
|
+
desc 'Sail ISA Description Language (https://github.com/rems-project/sail)'
|
9
|
+
tag 'sail'
|
10
|
+
filenames '*.sail'
|
11
|
+
|
12
|
+
id = /[a-zA-Z_?][a-zA-Z0-9_?#]*/
|
13
|
+
|
14
|
+
tyvar = /'[a-zA-Z_?][a-zA-Z0-9_?#]*/
|
15
|
+
|
16
|
+
# We are careful with the definition of operators to ensure openers
|
17
|
+
# like // and /* cannot prefix valid operators
|
18
|
+
op_char = '[!%&*+-./:<=>@^|]'
|
19
|
+
op_char_no_slash = '[!%&*+-.:<=>@^|]'
|
20
|
+
op_char_no_slash_star = '[!%&+-.:<=>@^|]'
|
21
|
+
|
22
|
+
# Sail operators can be suffixed with an underscore followed by a
|
23
|
+
# regular identifier i.e. <=_u for unsigned less that or equal to
|
24
|
+
op_suffix = "#{op_char}*(_#{id.source})"
|
25
|
+
|
26
|
+
# Operators of length 1, 2, and n > 2
|
27
|
+
operator1 = Regexp.new(op_char)
|
28
|
+
operator2 = Regexp.new("(#{op_char}#{op_char_no_slash_star})|(#{op_char_no_slash}#{op_char})")
|
29
|
+
operatorn = Regexp.new("(#{op_char}#{op_char_no_slash_star}#{op_suffix})|(#{op_char_no_slash}#{op_char}#{op_suffix})")
|
30
|
+
|
31
|
+
def self.keywords
|
32
|
+
@keywords ||= Set.new %w[
|
33
|
+
and as by match clause operator default end enum else forall foreach function mapping overload throw
|
34
|
+
try catch if in let var ref pure monadic register return scattered struct then type union newtype with
|
35
|
+
val outcome instantiation impl repeat until while do bitfield forwards backwards to
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Keywords that appear in types, and builtin special types
|
40
|
+
def self.keywords_type
|
41
|
+
@keywords_type ||= Set.new %w[
|
42
|
+
dec inc Int Order Bool Type bits bool int option unit implicit
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
# These keywords appear like special functions rather than regular
|
47
|
+
# keywords, i.e. `assert(cond, "message")`
|
48
|
+
def self.builtins
|
49
|
+
@builtins ||= Set.new %w[
|
50
|
+
bitzero bitone exit false sizeof constraint true undefined
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Reserved and internal keywords, as well as deprecated keywords
|
55
|
+
def self.reserved
|
56
|
+
@reserved ||= Set.new %w[
|
57
|
+
effect cast constant import module mutual configuration termination_measure internal_plet internal_return
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
state :whitespace do
|
62
|
+
rule(/\s+/, Text::Whitespace)
|
63
|
+
end
|
64
|
+
|
65
|
+
state :root do
|
66
|
+
mixin :whitespace
|
67
|
+
|
68
|
+
rule(/0x[0-9A-Fa-f_]+/, Num::Hex)
|
69
|
+
rule(/0b[0-1_]+/, Num::Bin)
|
70
|
+
rule(/[0-9]+\.[0-9]+/, Num::Float)
|
71
|
+
rule(/[0-9_]+/, Num::Integer)
|
72
|
+
|
73
|
+
rule(/"/, Str, :string)
|
74
|
+
|
75
|
+
rule tyvar, Name::Variable
|
76
|
+
|
77
|
+
rule(/(val\b)(\s+)(#{id})/) do
|
78
|
+
groups Keyword, Text::Whitespace, Name::Function
|
79
|
+
end
|
80
|
+
|
81
|
+
rule(/(function\b)(\s+)(#{id})/) do
|
82
|
+
groups Keyword, Text::Whitespace, Name::Function
|
83
|
+
end
|
84
|
+
|
85
|
+
rule %r{//}, Comment, :line_comment
|
86
|
+
rule %r{/\*}, Comment, :comment
|
87
|
+
rule(/\$\[/, Comment::Preproc, :attribute)
|
88
|
+
rule(/\$#{id}/, Comment::Preproc, :pragma)
|
89
|
+
|
90
|
+
# Function arrows
|
91
|
+
rule(/->/, Punctuation)
|
92
|
+
|
93
|
+
# Two character brackets
|
94
|
+
rule(/\[\|/, Punctuation)
|
95
|
+
rule(/\|\]/, Punctuation)
|
96
|
+
rule(/{\|/, Punctuation)
|
97
|
+
rule(/\|}/, Punctuation)
|
98
|
+
|
99
|
+
rule(/[,@=(){}\[\];:]/, Punctuation)
|
100
|
+
rule operatorn, Operator
|
101
|
+
rule operator2, Operator
|
102
|
+
rule operator1, Operator
|
103
|
+
|
104
|
+
rule id do |m|
|
105
|
+
name = m[0]
|
106
|
+
|
107
|
+
if self.class.keywords.include? name
|
108
|
+
token Keyword
|
109
|
+
elsif self.class.keywords_type.include? name
|
110
|
+
token Keyword::Type
|
111
|
+
elsif self.class.builtins.include? name
|
112
|
+
token Name::Builtin
|
113
|
+
elsif self.class.reserved.include? name
|
114
|
+
token Keyword::Reserved
|
115
|
+
else
|
116
|
+
token Name
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
state :string do
|
122
|
+
rule(/"/, Str, :pop!)
|
123
|
+
# Sail escape sequences are a subset of OCaml's https://v2.ocaml.org/manual/lex.html#escape-sequence
|
124
|
+
rule(/\\([\\ntbr"']|x[a-fA-F0-9]{2}|[0-7]{3})/, Str::Escape)
|
125
|
+
rule(/[^\\"\n]+/, Str)
|
126
|
+
end
|
127
|
+
|
128
|
+
state :attribute do
|
129
|
+
rule(/\]/, Comment::Preproc, :pop!)
|
130
|
+
rule(/[^\]]+/, Comment::Preproc)
|
131
|
+
end
|
132
|
+
|
133
|
+
state :pragma do
|
134
|
+
rule(/\n/, Text::Whitespace, :pop!)
|
135
|
+
rule(/[^\n]+/, Comment::Preproc)
|
136
|
+
end
|
137
|
+
|
138
|
+
state :line_comment do
|
139
|
+
rule(/\n/, Text::Whitespace, :pop!)
|
140
|
+
rule(/[^\n]+/, Comment)
|
141
|
+
end
|
142
|
+
|
143
|
+
state :comment do
|
144
|
+
rule %r{/\*}, Comment, :comment
|
145
|
+
rule %r{\*/}, Comment, :pop!
|
146
|
+
rule(/\n/, Text::Whitespace)
|
147
|
+
rule(/./, Comment)
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor
|
4
|
+
module Sail
|
5
|
+
module SourceMacro
|
6
|
+
# Should match Docinfo.docinfo_version in Sail OCaml source
|
7
|
+
VERSION = 1
|
8
|
+
PLUGIN_NAME = 'Sail Asciidoc plugin'
|
9
|
+
|
10
|
+
def get_sourcemap(doc, attrs)
|
11
|
+
from = attrs.delete('from') { 'sail-doc' }
|
12
|
+
source_map = doc.attr(from)
|
13
|
+
::Asciidoctor::Sail::Sources.register(from, source_map)
|
14
|
+
json = ::Asciidoctor::Sail::Sources.get(from)
|
15
|
+
if json['version'] != VERSION
|
16
|
+
raise "#{PLUGIN_NAME}: Version does not match version in source map #{source_map}"
|
17
|
+
end
|
18
|
+
|
19
|
+
json
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_type(attrs)
|
23
|
+
attrs.delete('type') { 'function' }
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_part(attrs)
|
27
|
+
attrs.delete('part') { 'source' }
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_split(attrs)
|
31
|
+
attrs.delete('split') { '' }
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_source(json, part)
|
35
|
+
source = ''
|
36
|
+
|
37
|
+
if json.is_a? String
|
38
|
+
source = json
|
39
|
+
elsif json[part].is_a? String
|
40
|
+
source = json[part]
|
41
|
+
else
|
42
|
+
file = File.read(json['file'])
|
43
|
+
loc = json[part]['loc']
|
44
|
+
|
45
|
+
# Get the source code, adjusting for the indentation of the first line of the span
|
46
|
+
indent = loc[2] - loc[1]
|
47
|
+
|
48
|
+
source = file.byteslice(loc[2], loc[5] - loc[2])
|
49
|
+
source = (' ' * indent) + source
|
50
|
+
end
|
51
|
+
|
52
|
+
source
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_sail_object(json, target, attrs)
|
56
|
+
type = get_type(attrs)
|
57
|
+
json = json["#{type}s"]
|
58
|
+
if json.nil?
|
59
|
+
raise "#{PLUGIN_NAME}: No Sail objects of type #{type}"
|
60
|
+
end
|
61
|
+
json = json[target]
|
62
|
+
if json.nil?
|
63
|
+
raise "#{PLUGIN_NAME}: No Sail #{type} #{target} could be found"
|
64
|
+
end
|
65
|
+
json = json[type]
|
66
|
+
|
67
|
+
if attrs.key? 'clause'
|
68
|
+
clause = attrs.delete('clause')
|
69
|
+
json.each do |child|
|
70
|
+
if match_clause(clause, child['pattern'])
|
71
|
+
json = child
|
72
|
+
break
|
73
|
+
end
|
74
|
+
end
|
75
|
+
elsif attrs.key? 'left-clause'
|
76
|
+
clause = attrs.delete('left-clause')
|
77
|
+
json.each do |child|
|
78
|
+
if match_clause(clause, child['left'])
|
79
|
+
json = child
|
80
|
+
break
|
81
|
+
end
|
82
|
+
end
|
83
|
+
elsif attrs.key? 'right-clause'
|
84
|
+
clause = attrs.delete('right-clause')
|
85
|
+
json.each do |child|
|
86
|
+
if match_clause(clause, child['right'])
|
87
|
+
json = child
|
88
|
+
break
|
89
|
+
end
|
90
|
+
end
|
91
|
+
elsif attrs.key? 'grep'
|
92
|
+
grep = attrs.delete('grep')
|
93
|
+
json.each do |child|
|
94
|
+
source = read_source(child, 'body')
|
95
|
+
if source =~ Regexp.new(grep)
|
96
|
+
json = child
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
json
|
102
|
+
end
|
103
|
+
|
104
|
+
# Compute the minimum indentation for any line in a source block
|
105
|
+
def minindent(tabwidth, source)
|
106
|
+
indent = -1
|
107
|
+
source.each_line do |line|
|
108
|
+
line_indent = 0
|
109
|
+
line.chars.each do |c|
|
110
|
+
case c
|
111
|
+
when ' '
|
112
|
+
line_indent += 1
|
113
|
+
when "\t"
|
114
|
+
line_indent += tabwidth
|
115
|
+
else
|
116
|
+
break
|
117
|
+
end
|
118
|
+
end
|
119
|
+
indent = line_indent if indent == -1 || line_indent < indent
|
120
|
+
end
|
121
|
+
indent
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_source(doc, target, attrs)
|
125
|
+
json = get_sourcemap doc, attrs
|
126
|
+
json = get_sail_object json, target, attrs
|
127
|
+
dedent = attrs.any? { |k, v| (k.is_a? Integer) && %w[dedent unindent].include?(v) }
|
128
|
+
strip = attrs.any? { |k, v| (k.is_a? Integer) && %w[trim strip].include?(v) }
|
129
|
+
|
130
|
+
part = get_part attrs
|
131
|
+
split = get_split attrs
|
132
|
+
|
133
|
+
source = ''
|
134
|
+
if split != ''
|
135
|
+
source = json['splits'][split]
|
136
|
+
else
|
137
|
+
source = read_source(json, part)
|
138
|
+
end
|
139
|
+
|
140
|
+
source.strip! if strip
|
141
|
+
|
142
|
+
if dedent
|
143
|
+
lines = ''
|
144
|
+
min = minindent 4, source
|
145
|
+
|
146
|
+
source.each_line do |line|
|
147
|
+
lines += line[min..]
|
148
|
+
end
|
149
|
+
source = lines
|
150
|
+
end
|
151
|
+
|
152
|
+
source
|
153
|
+
end
|
154
|
+
|
155
|
+
def match_clause(desc, json)
|
156
|
+
if desc =~ /^([a-zA-Z_?][a-zA-Z0-9_?#]*)(\(.*\))$/
|
157
|
+
return false unless json['type'] == 'app' && json['id'] == ::Regexp.last_match(1)
|
158
|
+
|
159
|
+
patterns = json['patterns']
|
160
|
+
if patterns.length == 1
|
161
|
+
patterns = patterns[0]
|
162
|
+
end
|
163
|
+
|
164
|
+
match_clause ::Regexp.last_match(2), patterns
|
165
|
+
elsif desc.length.positive? && desc[0] == '('
|
166
|
+
tuples = nil
|
167
|
+
if json.is_a? Array
|
168
|
+
tuples = json
|
169
|
+
elsif json['type'] == 'tuple'
|
170
|
+
tuples = json['patterns']
|
171
|
+
else
|
172
|
+
tuples = [json]
|
173
|
+
end
|
174
|
+
|
175
|
+
results = []
|
176
|
+
desc[1...-1].split(',').each_with_index do |desc, i|
|
177
|
+
results.push(match_clause(desc.strip, tuples[i]))
|
178
|
+
end
|
179
|
+
results.all?
|
180
|
+
elsif desc == '_'
|
181
|
+
true
|
182
|
+
elsif desc =~ /^([a-zA-Z_?][a-zA-Z0-9_?#]*)$/
|
183
|
+
json['type'] == 'id' && json['id'] == ::Regexp.last_match(1)
|
184
|
+
elsif desc =~ /^(0[bx][a-fA-F0-9]*)$/
|
185
|
+
json['type'] == 'literal' && json['value'] == ::Regexp.last_match(1)
|
186
|
+
else
|
187
|
+
false
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class SourceBlockMacro < ::Asciidoctor::Extensions::BlockMacroProcessor
|
193
|
+
include SourceMacro
|
194
|
+
|
195
|
+
use_dsl
|
196
|
+
|
197
|
+
named :sail
|
198
|
+
|
199
|
+
def process(parent, target, attrs)
|
200
|
+
source = get_source parent.document, target, attrs
|
201
|
+
|
202
|
+
create_listing_block parent, source, { 'style' => 'source', 'language' => 'sail' }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class SourceIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
207
|
+
include SourceMacro
|
208
|
+
|
209
|
+
def handles? target
|
210
|
+
target.start_with? 'sail:'
|
211
|
+
end
|
212
|
+
|
213
|
+
def process doc, reader, target, attrs
|
214
|
+
target.delete_prefix! 'sail:'
|
215
|
+
|
216
|
+
source = get_source doc, target, attrs
|
217
|
+
|
218
|
+
reader.push_include source, target, target, 1, {}
|
219
|
+
reader
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class WavedromIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
224
|
+
include SourceMacro
|
225
|
+
|
226
|
+
def handles? target
|
227
|
+
target.start_with? 'sailwavedrom:'
|
228
|
+
end
|
229
|
+
|
230
|
+
def process doc, reader, target, attrs
|
231
|
+
target.delete_prefix! 'sailwavedrom:'
|
232
|
+
json = get_sourcemap doc, attrs
|
233
|
+
json = get_sail_object json, target, attrs
|
234
|
+
|
235
|
+
key = 'wavedrom'
|
236
|
+
if attrs.any? { |k, v| (k.is_a? Integer) && v == 'right' }
|
237
|
+
key = 'right_wavedrom'
|
238
|
+
elsif attrs.any? { |k, v| (k.is_a? Integer) && v == 'left' }
|
239
|
+
key = 'left_wavedrom'
|
240
|
+
end
|
241
|
+
|
242
|
+
if attrs.key? 'raw'
|
243
|
+
diagram = json[key]
|
244
|
+
else
|
245
|
+
diagram = "[wavedrom, , svg]\n....\n#{json[key]}\n...."
|
246
|
+
end
|
247
|
+
|
248
|
+
reader.push_include diagram, target, target, 1, {}
|
249
|
+
reader
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class DocCommentIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
254
|
+
include SourceMacro
|
255
|
+
|
256
|
+
def handles? target
|
257
|
+
target.start_with? 'sailcomment:'
|
258
|
+
end
|
259
|
+
|
260
|
+
def process doc, reader, target, attrs
|
261
|
+
target.delete_prefix! 'sailcomment:'
|
262
|
+
json = get_sourcemap doc, attrs
|
263
|
+
json = get_sail_object json, target, attrs
|
264
|
+
|
265
|
+
if json.nil? || json.is_a?(Array)
|
266
|
+
raise "#{PLUGIN_NAME}: Could not find Sail object for #{target} when processing include::sailcomment. You may need to specify a clause."
|
267
|
+
end
|
268
|
+
|
269
|
+
comment = json['comment']
|
270
|
+
if comment.nil?
|
271
|
+
raise "#{PLUGIN_NAME}: No documentation comment for Sail object #{target}"
|
272
|
+
end
|
273
|
+
|
274
|
+
reader.push_include comment, target, target, 1, attrs
|
275
|
+
reader
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Asciidoctor
|
6
|
+
module Sail
|
7
|
+
class Sources
|
8
|
+
@sources = {}
|
9
|
+
|
10
|
+
def self.register(key, sourcemap_path)
|
11
|
+
return if @sources.key?(key)
|
12
|
+
|
13
|
+
file = File.read(sourcemap_path)
|
14
|
+
@sources[key] = JSON.parse(file)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.get(key)
|
18
|
+
@sources[key]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'asciidoctor/extensions'
|
4
|
+
|
5
|
+
require_relative 'asciidoctor-sail/sources'
|
6
|
+
require_relative 'asciidoctor-sail/macros'
|
7
|
+
require_relative 'asciidoctor-sail/highlighter'
|
8
|
+
|
9
|
+
Asciidoctor::Extensions.register do
|
10
|
+
block_macro Asciidoctor::Sail::SourceBlockMacro
|
11
|
+
include_processor Asciidoctor::Sail::SourceIncludeProcessor
|
12
|
+
include_processor Asciidoctor::Sail::DocCommentIncludeProcessor
|
13
|
+
include_processor Asciidoctor::Sail::WavedromIncludeProcessor
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: asciidoctor-sail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alasdair Armstrong
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Asciidoctor extension for documenting Sail models
|
14
|
+
email:
|
15
|
+
- alasdair.armstrong@cl.cam.ac.uk
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.adoc
|
22
|
+
- lib/asciidoctor-sail.rb
|
23
|
+
- lib/asciidoctor-sail/highlighter.rb
|
24
|
+
- lib/asciidoctor-sail/macros.rb
|
25
|
+
- lib/asciidoctor-sail/sources.rb
|
26
|
+
homepage: https://github.com/Alasdair/asciidoctor-sail
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubygems_version: 3.4.9
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: An Asciidoctor extension that supports including formatted Sail source in
|
49
|
+
ISA manuals
|
50
|
+
test_files: []
|