typedocs 0.0.1

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.
@@ -0,0 +1,126 @@
1
+ # Ruby argument pattern:
2
+ # - required* optional* (rest requied*)?
3
+ # - optional+ requied* # optional is matched forward-wise
4
+ #
5
+ # s1 +-opt-> s2 +--req--> s3
6
+ # | |
7
+ # +----------+---------+--rest--> s6 -req-> s7
8
+ # | / /
9
+ # `-req-> s4 -opt-> s5
10
+ class Typedocs::ArgumentsSpec
11
+ def initialize
12
+ # [[type, [spec ...]] ...]
13
+ @specs = []
14
+ @current = nil
15
+ end
16
+ def empty?
17
+ @specs.empty?
18
+ end
19
+ def valid?(args)
20
+ matched = match(args)
21
+ matched && matched.all? {|arg, spec| spec.valid? arg}
22
+ end
23
+ def error_message_for(args)
24
+ matched = match(args)
25
+ errors = matched.select{|arg, spec|!spec.valid?(arg)}
26
+ "Expected: #{to_source}. Errors: #{errors.map{|arg,spec|spec.error_message_for(arg)}.join(' ||| ')}"
27
+ end
28
+ def to_source
29
+ @specs.flat_map{|t,s|
30
+ attr =
31
+ case t
32
+ when :req
33
+ ''
34
+ when :opt
35
+ '?'
36
+ when :res
37
+ '*'
38
+ else
39
+ raise
40
+ end
41
+ s.map{|spec| "#{attr}#{spec.to_source}" }
42
+ }.join(' -> ')
43
+ end
44
+ def add_required(arg_spec)
45
+ _add :req, arg_spec
46
+ end
47
+ def add_optional(arg_spec)
48
+ _add :opt, arg_spec
49
+ end
50
+ def add_rest(arg_spec)
51
+ _add :res, arg_spec
52
+ end
53
+ private
54
+ # args:[...] -> success:[[arg,spec]...] | fail:nil
55
+ def match(args)
56
+ args = args.dup
57
+ types = @specs.map{|t,s|t}
58
+ case types
59
+ when [:opt, :req]
60
+ opt, req = @specs.map{|t,s|s}
61
+ return nil unless (req.length..(req.length+opt.length)) === args.size
62
+ args[0...-req.length].zip(opt).to_a + req.zip(args[-req.length..-1]).to_a
63
+ else
64
+ # [reqs, opts, rest, reqs]
65
+ partial = []
66
+ i = 0
67
+ if types[i] == :req
68
+ partial.push @specs[i][1]
69
+ i += 1
70
+ else
71
+ partial.push []
72
+ end
73
+ if types[i] == :opt
74
+ partial.push @specs[i][1]
75
+ i += 1
76
+ else
77
+ partial.push []
78
+ end
79
+ if types[i] == :res
80
+ partial.push @specs[i][1]
81
+ i += 1
82
+ else
83
+ partial.push []
84
+ end
85
+ if types[i] == :req
86
+ partial.push @specs[i][1]
87
+ i += 1
88
+ else
89
+ partial.push []
90
+ end
91
+ return nil unless i == types.length
92
+ reqs, opts, rest, reqs2 = partial
93
+ raise unless rest.length < 2
94
+
95
+ len_min = reqs.length + reqs2.length
96
+ if rest.empty?
97
+ len_max = reqs.length + opts.length + reqs2.length
98
+ return nil unless (len_min..len_max) === args.length
99
+ else
100
+ return nil unless len_min <= args.length
101
+ end
102
+ reqs_args = args.shift(reqs.length)
103
+ reqs2_args = args.pop(reqs2.length)
104
+ opts_args = args.shift([opts.length, args.length].min)
105
+ rest_args = args
106
+
107
+ rest_spec = rest[0]
108
+ return [
109
+ *reqs_args.zip(reqs),
110
+ *opts_args.zip(opts),
111
+ *(rest_spec ? rest_args.map{|a|[a, rest_spec]} : []),
112
+ *reqs2_args.zip(reqs2),
113
+ ]
114
+ end
115
+ end
116
+ def _add(type,spec)
117
+ Typedocs.ensure_klass(spec, Typedocs::TypeSpec)
118
+ if @current == type
119
+ @specs.last[1].push spec
120
+ else
121
+ @specs.push [type, [spec]]
122
+ @current = type
123
+ end
124
+ end
125
+ end
126
+
@@ -0,0 +1,51 @@
1
+ module Typedocs
2
+ class BlockSpec
3
+ def initialize(type, name)
4
+ raise ArgumentError unless [:req, :opt, :none].include?(type)
5
+ @name = name
6
+ @type = type
7
+ end
8
+ attr_reader :name
9
+ def valid?(block)
10
+ case block
11
+ when nil
12
+ return @type == :opt || @type == :none
13
+ when Proc
14
+ return @type == :opt || @type == :req
15
+ else
16
+ raise 'maybe typedocs bug'
17
+ end
18
+ end
19
+ def error_message_for(block)
20
+ raise ArgumentError if valid?(block)
21
+ case @type
22
+ when :req
23
+ "Block not given"
24
+ when :none
25
+ "Block not allowed"
26
+ else
27
+ raise 'maybe typedocs bug'
28
+ end
29
+ end
30
+ def to_source
31
+ n = name
32
+ case @type
33
+ when :req
34
+ "&#{n}"
35
+ when :opt
36
+ "?&#{n}"
37
+ when :none
38
+ ''
39
+ else
40
+ raise "Invalid type: #{@type}"
41
+ end
42
+ end
43
+ def to_source_with_arrow
44
+ if @type == :none
45
+ ''
46
+ else
47
+ "#{to_source} -> "
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ class Typedocs::Context
2
+ def self.valid_udt_name?(name)
3
+ /\A@[A-Z][a-zA-Z0-9]*\Z/ =~ name.to_s
4
+ end
5
+
6
+ def initialize(klass)
7
+ @klass = klass
8
+ # udt_name => spec
9
+ @specs = {}
10
+ end
11
+ def typedef(name, definition)
12
+ raise ArgumentError, "Invalid user-defined type name: #{name}" unless self.class.valid_udt_name?(name)
13
+ @specs[name.to_s] = Typedocs::Parser.new.parse(@klass, definition, :type)
14
+ end
15
+ def defined_type!(name)
16
+ self_defined_type(name) || outer_defined_type(name) || (raise Typedocs::NoSuchType, "Type not found in #{@klass.name}: #{name}")
17
+ end
18
+ def defined_type(name)
19
+ self_defined_type(name) || outer_defined_type(name) || parent_defined_type(name)
20
+ end
21
+ def self_defined_type(name)
22
+ @specs[name]
23
+ end
24
+ def outer_defined_type(name)
25
+ return nil unless @klass.name
26
+ outer_name = @klass.name.split(/::/)[0..-2]
27
+ unless outer_name.empty?
28
+ outer_klass = outer_name.inject(::Object) {|ns, name| ns.const_get(name) }
29
+ Typedocs.context(outer_klass).defined_type(name)
30
+ end
31
+ end
32
+ def parent_defined_type(name)
33
+ return nil unless @kass.kind_of? ::Class
34
+ superclass = @klass.superclass
35
+ return nil unless superclass
36
+ Typedocs.context(superclass).defined_type(name)
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ module Typedocs::DSL
2
+ def self.included(klass)
3
+ # doc:String | special:(:inherit) | nil
4
+ @typedocs_current_def = nil
5
+
6
+ class << klass
7
+ def tdoc(*args)
8
+ if args.size == 1 && args[0].is_a?(String) || args[0].is_a?(Symbol)
9
+ @typedocs_current_def = args[0]
10
+ elsif args.size == 0
11
+ Typedocs::MultiFunctionalInterface.new(self)
12
+ else
13
+ raise ArgumentError
14
+ end
15
+ end
16
+
17
+ def method_added(name)
18
+ return unless @typedocs_current_def
19
+
20
+ method_spec = ::Typedocs.create_method_spec(self, name, @typedocs_current_def)
21
+ @typedocs_current_def = nil
22
+
23
+ ::Typedocs.define_spec self, name, method_spec
24
+ end
25
+
26
+ def singleton_method_added(name)
27
+ return unless @typedocs_current_def
28
+
29
+ method_spec = ::Typedocs.create_method_spec(self, name, @typedocs_current_def)
30
+ @typedocs_current_def = nil
31
+
32
+ singleton_class = class << self; self; end
33
+ ::Typedocs.define_spec singleton_class, name, method_spec
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1 @@
1
+ TYPEDOCS_ENABLED = 1
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'typedocs'
3
+ rescue LoadError
4
+ require 'typedocs/fallback/impl'
5
+ Typedocs = TypedocsFallback
6
+ end
@@ -0,0 +1,19 @@
1
+ module TypedocsFallback
2
+ def self.method_missing(name, *args)
3
+ nil
4
+ end
5
+
6
+ NULL_OBJECT = BasicObject.new
7
+ def NULL_OBJECT.method_missing(name, *args)
8
+ self
9
+ end
10
+
11
+ module DSL
12
+ def self.included(klass)
13
+ def klass.tdoc(*args)
14
+ NULL_OBJECT
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,45 @@
1
+ module Typedocs::GrammerPrinter
2
+ def self.print_grammer(out)
3
+ parser = Typedocs::Parser::ASTBuilder.new
4
+ print_recursive(out, parser.root)
5
+ end
6
+
7
+ def self.print_recursive(out, root)
8
+ waiting = [root]
9
+ ignore = {}
10
+
11
+ until waiting.empty?
12
+ entity = waiting.pop
13
+ next if ignore[entity]
14
+ ignore[entity] = true
15
+
16
+ if entity.kind_of?(Parslet::Atoms::Entity)
17
+ out.puts "#{'%20s' % entity.to_s} <- #{entity.parslet.to_s.gsub(/[a-z_]+:/, '')}"
18
+ end
19
+
20
+ atoms = Parslet::Atoms
21
+
22
+ case entity
23
+ when atoms::Sequence
24
+ entity.parslets.reverse.each do|pl|
25
+ waiting.push pl
26
+ end
27
+ when atoms::Lookahead
28
+ waiting.push entity.bound_parslet
29
+ when atoms::Repetition
30
+ waiting.push entity.parslet
31
+ when atoms::Alternative
32
+ entity.alternatives.reverse.each do|pl|
33
+ waiting.push pl
34
+ end
35
+ when atoms::Entity
36
+ waiting.push entity.parslet
37
+ when atoms::Named
38
+ waiting.push entity.parslet
39
+ when atoms::Re, atoms::Str
40
+ else
41
+ raise "Unsupported class: #{entity.class}"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,88 @@
1
+ module Typedocs
2
+ module MethodSpec
3
+ class AnyOf
4
+ # [MethodSpec::Single] ->
5
+ def initialize(specs)
6
+ specs.each do|spec|
7
+ Typedocs.ensure_klass(spec, Typedocs::MethodSpec::Single)
8
+ end
9
+ @specs = specs
10
+ end
11
+
12
+ def call_with_validate(method, *args, &block)
13
+ spec = nil
14
+ @specs.each do|s|
15
+ begin
16
+ s.validate_caller(args, block)
17
+ spec = s
18
+ break
19
+ rescue Typedocs::ArgumentError, Typedocs::BlockError
20
+ end
21
+ end
22
+
23
+ unless spec
24
+ raise Typedocs::ArgumentError, "Arguments not match any rule"
25
+ end
26
+
27
+ ret = method.call(*args, &block)
28
+ spec.validate_retval ret
29
+ ret
30
+ end
31
+
32
+ def to_source
33
+ @specs.map(&:to_source).join(' || ')
34
+ end
35
+ end
36
+
37
+ class Single
38
+ def initialize(args_spec, block_spec, retval_spec)
39
+ Typedocs.ensure_klass(args_spec, Typedocs::ArgumentsSpec)
40
+ Typedocs.ensure_klass(block_spec, Typedocs::BlockSpec)
41
+ Typedocs.ensure_klass(retval_spec, Typedocs::TypeSpec)
42
+ @arguments_spec = args_spec
43
+ @block_spec = block_spec
44
+ @retval_spec = retval_spec
45
+ end
46
+
47
+ attr_reader :arguments_spec
48
+ attr_reader :block_spec
49
+ attr_reader :retval_spec
50
+
51
+ def call_with_validate(method, *args, &block)
52
+ validate_args args
53
+ validate_block block
54
+ ret = method.call *args, &block
55
+ validate_retval ret
56
+ ret
57
+ end
58
+
59
+ def validate_caller(args, block)
60
+ validate_args args
61
+ validate_block block
62
+ end
63
+
64
+ def validate_args(args)
65
+ raise Typedocs::ArgumentError, arguments_spec.error_message_for(args) unless arguments_spec.valid?(args)
66
+ end
67
+
68
+ def validate_block(block)
69
+ raise Typedocs::BlockError, "Cant accept block" if !block_spec && block
70
+ if block_spec
71
+ raise Typedocs::BlockError, block_spec.error_message_for(block) unless block_spec.valid?(block)
72
+ end
73
+ end
74
+
75
+ def validate_retval(ret)
76
+ raise Typedocs::RetValError, retval_spec.error_message_for(ret) unless retval_spec.valid?(ret)
77
+ end
78
+
79
+ def to_source
80
+ s = ''
81
+ s << arguments_spec.to_source
82
+ s << " -> " unless arguments_spec.empty?
83
+ s << block_spec.to_source_with_arrow
84
+ s << "#{retval_spec.to_source}"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding:utf-8 -*-
2
+
3
+ class Typedocs::MultiFunctionalInterface
4
+ def initialize(klass)
5
+ @klass = klass
6
+ end
7
+ def typedef(typename, definition)
8
+ Typedocs.context(@klass).typedef(typename, definition)
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ class Typedocs::Parser; end
2
+
3
+ require 'typedocs/parser/ast_builder'
4
+ require 'typedocs/parser/object_builder'
5
+
6
+ class Typedocs::Parser
7
+ def parse(klass, src, type = :root)
8
+ ast = Typedocs::Parser::ASTBuilder.new
9
+ obj = Typedocs::Parser::ObjectBuilder.create_builder_for(klass)
10
+
11
+ root = ast.public_send(type)
12
+
13
+ result =
14
+ begin
15
+ obj.apply root.parse(src)
16
+ rescue Parslet::ParseFailed => e
17
+ raise e.cause.ascii_tree
18
+ rescue ArgumentError => e
19
+ error = StandardError.new("Parse error: Maybe parser's bug. Input=#{src.inspect}, Error = #{e}")
20
+ error.set_backtrace e.backtrace
21
+ raise error
22
+ end
23
+ if result.is_a?(Hash)
24
+ raise "Parse error: Maybe parser's bug or unexpected argument(type: #{type.inspect}). Input=#{src.inspect}, Result=#{result.inspect}"
25
+ end
26
+ result
27
+ end
28
+ end