typedocs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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