jaina 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +19 -0
  4. data/README.md +160 -1
  5. data/jaina.gemspec +2 -1
  6. data/lib/jaina/parser/ast/context.rb +98 -0
  7. data/lib/jaina/parser/ast/evaluator.rb +17 -0
  8. data/lib/jaina/parser/ast/tree.rb +35 -0
  9. data/lib/jaina/parser/ast/tree_builder.rb +129 -0
  10. data/lib/jaina/parser/ast.rb +62 -0
  11. data/lib/jaina/parser/code_converter/to_postfix_form.rb +167 -0
  12. data/lib/jaina/parser/code_converter/to_prefix_form.rb +122 -0
  13. data/lib/jaina/parser/code_converter.rb +28 -0
  14. data/lib/jaina/parser/expression/operator/abstract/dsl.rb +256 -0
  15. data/lib/jaina/parser/expression/operator/abstract.rb +67 -0
  16. data/lib/jaina/parser/expression/operator/and.rb +12 -0
  17. data/lib/jaina/parser/expression/operator/grouping/dsl.rb +100 -0
  18. data/lib/jaina/parser/expression/operator/grouping.rb +12 -0
  19. data/lib/jaina/parser/expression/operator/left_corner.rb +10 -0
  20. data/lib/jaina/parser/expression/operator/non_terminal.rb +25 -0
  21. data/lib/jaina/parser/expression/operator/not.rb +12 -0
  22. data/lib/jaina/parser/expression/operator/or.rb +12 -0
  23. data/lib/jaina/parser/expression/operator/right_corner.rb +10 -0
  24. data/lib/jaina/parser/expression/operator/terminal.rb +25 -0
  25. data/lib/jaina/parser/expression/operator.rb +15 -0
  26. data/lib/jaina/parser/expression/registry/access_interface_mixin.rb +49 -0
  27. data/lib/jaina/parser/expression/registry.rb +127 -0
  28. data/lib/jaina/parser/expression.rb +87 -0
  29. data/lib/jaina/parser/tokenizer.rb +37 -0
  30. data/lib/jaina/parser.rb +44 -0
  31. data/lib/jaina/version.rb +5 -1
  32. data/lib/jaina.rb +58 -4
  33. metadata +29 -2
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module Jaina::Parser::Expression::Operator::Abstract::DSL
6
+ class << self
7
+ # @param base_klass [Class]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def included(base_klass)
13
+ base_klass.instance_variable_set(:@acts_as_group_closener, false)
14
+ base_klass.instance_variable_set(:@acts_as_group_opener, false)
15
+
16
+ base_klass.extend ClassMethods
17
+ base_klass.include InstanceMethods
18
+ base_klass.singleton_class.prepend(ClassInheritance)
19
+ end
20
+ end
21
+
22
+ # @api private
23
+ # @since 0.1.0
24
+ module ClassInheritance
25
+ # @param child_klass [Class]
26
+ # @return [void]
27
+ #
28
+ # @api private
29
+ # @since 0.1.0
30
+ def inherited(child_klass)
31
+ child_klass.instance_variable_set(
32
+ :@acts_as_group_opener,
33
+ instance_variable_get(:@acts_as_group_opener)
34
+ )
35
+
36
+ child_klass.instance_variable_set(
37
+ :@acts_as_group_closener,
38
+ instance_variable_get(:@acts_as_group_closener)
39
+ )
40
+
41
+ super
42
+ end
43
+ end
44
+
45
+ # @api private
46
+ # @since 0.1.0
47
+ module ClassMethods
48
+ # @return [void]
49
+ #
50
+ # @api private
51
+ # @since 0.1.0
52
+ def acts_as_group_closener
53
+ @acts_as_group_closener = true
54
+ end
55
+
56
+ # @return [void]
57
+ #
58
+ # @api private
59
+ # @since 0.1.0
60
+ def acts_as_group_opener
61
+ @acts_as_group_opener = true
62
+ end
63
+
64
+ # @return [void]
65
+ #
66
+ # @api private
67
+ # @since 0.1.0
68
+ def acts_as_group_closener?
69
+ @acts_as_group_closener
70
+ end
71
+
72
+ # @return [void]
73
+ #
74
+ # @api private
75
+ # @since 0.1.0
76
+ def acts_as_group_opener?
77
+ @acts_as_group_opener
78
+ end
79
+ end
80
+
81
+ # @api private
82
+ # @since 0.1.0
83
+ module InstanceMethods
84
+ # @return [Boolean]
85
+ #
86
+ # @api private
87
+ # @since 0.1.0
88
+ def acts_as_group_closener?
89
+ self.class.acts_as_group_closener?
90
+ end
91
+
92
+ # @return [Boolean]
93
+ #
94
+ # @api private
95
+ # @since 0.1.0
96
+ def acts_as_group_opener?
97
+ self.class.acts_as_group_opener?
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class Grouping < Abstract
7
+ require_relative './grouping/dsl'
8
+
9
+ # @since 0.1.0
10
+ include DSL
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class LeftCorner < Grouping
7
+ acts_as_group_opener
8
+ token '('
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class NonTerminal < Abstract
7
+ class << self
8
+ # @return [Boolean]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def terminal?
13
+ false
14
+ end
15
+
16
+ # @return [Boolean]
17
+ #
18
+ # @api private
19
+ # @since 0.1.0
20
+ def non_terminal?
21
+ true
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class Not < NonTerminal
7
+ precedence_level 4
8
+ associativity_direction :right
9
+ token 'NOT'
10
+ acts_as_unary_term
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class Or < NonTerminal
7
+ precedence_level 2
8
+ associativity_direction :left
9
+ token 'OR'
10
+ acts_as_binary_term
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class RightCorner < Grouping
7
+ acts_as_group_closener
8
+ token ')'
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression::Operator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class Terminal < Abstract
7
+ class << self
8
+ # @return [Boolean]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def terminal?
13
+ true
14
+ end
15
+
16
+ # @return [Boolean]
17
+ #
18
+ # @api private
19
+ # @since 0.1.0
20
+ def non_temrinal?
21
+ false
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module Jaina::Parser::Expression::Operator
6
+ require_relative './operator/abstract'
7
+ require_relative './operator/non_terminal'
8
+ require_relative './operator/terminal'
9
+ require_relative './operator/grouping'
10
+ require_relative './operator/and'
11
+ require_relative './operator/or'
12
+ require_relative './operator/not'
13
+ require_relative './operator/left_corner'
14
+ require_relative './operator/right_corner'
15
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jaina::Parser::Expression
4
+ class Registry
5
+ # @api private
6
+ # @since 0.1.0
7
+ module AccessInterfaceMixin
8
+ class << self
9
+ # @param base_klass [Class]
10
+ # @return [void]
11
+ def included(base_klass)
12
+ base_klass.instance_variable_set(:@__expression_registry__, Registry.new)
13
+ base_klass.extend(ClassMethods)
14
+ end
15
+ end
16
+
17
+ # @api private
18
+ # @since 0.1.0
19
+ module ClassMethods
20
+ # @return [Array<String>]
21
+ #
22
+ # @api private
23
+ # @since 0.1.0
24
+ def expressions
25
+ @__expression_registry__.expressions
26
+ end
27
+
28
+ # @param extension_token [String]
29
+ # @return [Class{Jaina::Parser::Expressions::Operator::Abstract}]
30
+ #
31
+ # @api private
32
+ # @since 0.1.0
33
+ def [](extension_token)
34
+ @__expression_registry__[extension_token]
35
+ end
36
+ alias_method :fetch, :[]
37
+
38
+ # @param expression [Class{Jaina::Parser::Expressions::Operator::Abstract}]
39
+ # @return [void]
40
+ #
41
+ # @api private
42
+ # @since 0.1.0
43
+ def register(expression)
44
+ @__expression_registry__.register(expression)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class Jaina::Parser::Expression::Registry
6
+ require_relative './registry/access_interface_mixin'
7
+
8
+ # @since 0.1.0
9
+ Error = Class.new(StandardError)
10
+ # @since 0.1.0
11
+ AlreadyRegisteredExpressionError = Class.new(Error)
12
+ # @since 0.1.0
13
+ UnregisteredExpressionError = Class.new(Error)
14
+ # @since 0.1.0
15
+ IncorrectExpressionObjectError = Class.new(Error)
16
+
17
+ # @return [void]
18
+ #
19
+ # @api private
20
+ # @since 0.1.0
21
+ def initialize
22
+ @expression_set = {}
23
+ @access_lock = Mutex.new
24
+ end
25
+
26
+ # @param expression_token [String]
27
+ # @return [Class{Jaina::Parser::Operator::Abstract}]
28
+ #
29
+ # @api private
30
+ # @since 0.1.0
31
+ def [](expression_token)
32
+ thread_safe { fetch(expression_token) }
33
+ end
34
+
35
+ # @param expression [Class{Jaina::Parser::Operator::Abstract}]
36
+ # @return [void]
37
+ #
38
+ # @api private
39
+ # @since 0.1.0
40
+ def register(expression)
41
+ thread_safe { apply(expression) }
42
+ end
43
+
44
+ # @return [Array<String>]
45
+ #
46
+ # @api private
47
+ # @since 0.1.0
48
+ def expressions
49
+ thread_safe { expression_names }
50
+ end
51
+
52
+ private
53
+
54
+ # @return [Hash<String,Class{Jaina::Parser::Operator::Abstract}>]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ attr_reader :expression_set
59
+
60
+ # @return [Mutex]
61
+ #
62
+ # @api private
63
+ # @since 0.1.0
64
+ attr_reader :access_lock
65
+
66
+ # @return [void]
67
+ #
68
+ # @api private
69
+ # @since 0.1.0
70
+ def thread_safe
71
+ access_lock.synchronize { yield if block_given? }
72
+ end
73
+
74
+ # @param expression_token [String]
75
+ # @return [Boolean]
76
+ #
77
+ # @api private
78
+ # @since 0.1.0
79
+ def registered?(expression_token)
80
+ expression_set.key?(expression_token)
81
+ end
82
+
83
+ # @return [Array<String>]
84
+ #
85
+ # @api private
86
+ # @since 0.1.0
87
+ def expression_names
88
+ expression_set.keys
89
+ end
90
+
91
+ # @param expression [Class{Jaina::Parser::Operator::Abstract}]
92
+ # @return [void]
93
+ #
94
+ # @raise [Jaina::Parser::Expression::Registry::AlreadyRegisteredExpressionError]
95
+ #
96
+ # @api private
97
+ # @since 0.1.0
98
+ def apply(expression)
99
+ raise(
100
+ AlreadyRegisteredExpressionError,
101
+ "Expression with token `#{expression.token}` already exist"
102
+ ) if registered?(expression.token)
103
+
104
+ raise(
105
+ IncorrectExpressionObjectError,
106
+ 'Expression should be a subtype of Jaina::Parser::Expression::Operation::Abstract'
107
+ ) unless expression.is_a?(Class) && expression < Jaina::Parser::Expression::Operator::Abstract
108
+
109
+ expression_set[expression.token] = expression
110
+ end
111
+
112
+ # @param expression_token [String]
113
+ # @return [Class{Jaina::Parser::Operator::Abstract}]
114
+ #
115
+ # @raise [Jaina::Parser::Expression::Registry::UnregisteredExpressionError]
116
+ #
117
+ # @api private
118
+ # @since 0.1.0
119
+ def fetch(expression_token)
120
+ raise(
121
+ UnregisteredExpressionError,
122
+ "Expression with token `#{expression_token}` is not registered"
123
+ ) unless registered?(expression_token)
124
+
125
+ expression_set[expression_token]
126
+ end
127
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module Jaina::Parser::Expression
6
+ require_relative './expression/operator'
7
+ require_relative './expression/registry'
8
+
9
+ # @since 0.1.0
10
+ include Registry::AccessInterfaceMixin
11
+
12
+ # @since 0.1.0
13
+ register(Operator::And)
14
+ # @since 0.1.0
15
+ register(Operator::Not)
16
+ # @since 0.1.0
17
+ register(Operator::Or)
18
+ # @since 0.1.0
19
+ register(Operator::LeftCorner)
20
+ # @since 0.1.0
21
+ register(Operator::RightCorner)
22
+
23
+ class << self
24
+ # @param expression_token [String]
25
+ # @param arguments [Array<Any>]
26
+ # @return [Jaina::Parser::Expression::Operator::Abstract]
27
+ #
28
+ # @api private
29
+ # @since 0.1.0
30
+ def build(expression_token, *arguments)
31
+ expression = fetch(expression_token)
32
+ arguments.any? ? expression.new(*arguments) : expression.new
33
+ end
34
+
35
+ # @param expression_token [String]
36
+ # @return [Boolean]
37
+ #
38
+ # @api private
39
+ # @since 0.1.0
40
+ def terminal?(expression_token)
41
+ fetch(expression_token).terminal?
42
+ end
43
+
44
+ # @param expression_token [String]
45
+ # @return [Boolean]
46
+ #
47
+ # @api private
48
+ # @since 0.1.0
49
+ def non_terminal?(expression_token)
50
+ fetch(expression_token).non_terminal?
51
+ end
52
+
53
+ # @param expression_token [String]
54
+ # @return [Boolean]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ def group_opener?(expression_token)
59
+ fetch(expression_token).acts_as_group_opener?
60
+ end
61
+
62
+ # @param expression_token [String]
63
+ # @return [Boolean]
64
+ #
65
+ # @api private
66
+ # @since 0.1.0
67
+ def group_closener?(expression_token)
68
+ fetch(expression_token).acts_as_group_closener?
69
+ end
70
+
71
+ # @return [Boolean]
72
+ #
73
+ # @api private
74
+ # @since 0.1.0
75
+ def acts_as_binary_term?(expression_token)
76
+ fetch(expression_token).acts_as_binary_term?
77
+ end
78
+
79
+ # @return [Boolean]
80
+ #
81
+ # @api private
82
+ # @since 0.1.0
83
+ def acts_as_unary_term?(expression_token)
84
+ fetch(expression_token).acts_as_unary_term?
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module Jaina::Parser::Tokenizer
6
+ # @return [Regexp]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ TOKEN_SCAN_PATTERN = /\(|\)|[\w\.\*]+/.freeze
11
+
12
+ # @return [String]
13
+ #
14
+ # @api private
15
+ # @since 0.1.0
16
+ TOKEN_SPLITTER = ' '
17
+
18
+ class << self
19
+ # @param program [String]
20
+ # @return [Array<String>]
21
+ #
22
+ # @api private
23
+ # @since 0.1.0
24
+ def tokenize(program)
25
+ program.scan(TOKEN_SCAN_PATTERN)
26
+ end
27
+
28
+ # @param tokens [Array<String>]
29
+ # @return [String]
30
+ #
31
+ # @api private
32
+ # @since 0.1.0
33
+ def join(tokens)
34
+ tokens.join(TOKEN_SPLITTER)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class Jaina::Parser
6
+ require_relative './parser/expression'
7
+ require_relative './parser/tokenizer'
8
+ require_relative './parser/code_converter'
9
+ require_relative './parser/ast'
10
+
11
+ class << self
12
+ # @param program [String]
13
+ # @return [Jaina::Parser::AST]
14
+ #
15
+ # @api private
16
+ # @since 0.1.0
17
+ def parse(program)
18
+ new(program).parse
19
+ end
20
+ end
21
+
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ # @since 0.1.0
26
+ attr_reader :program
27
+
28
+ # @param program [String]
29
+ # @return [void]
30
+ #
31
+ # @api private
32
+ # @since 0.1.0
33
+ def initialize(program)
34
+ @program = program.dup.tap(&:freeze)
35
+ end
36
+
37
+ # @return [Jaina::Parser::AST]
38
+ #
39
+ # @api private
40
+ # @since 0.1.0
41
+ def parse
42
+ AST.build(program)
43
+ end
44
+ end
data/lib/jaina/version.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jaina
4
- VERSION = '0.0.0'
4
+ # @return [String]
5
+ #
6
+ # @api public
7
+ # @since 0.0.0
8
+ VERSION = '0.1.0'
5
9
  end
data/lib/jaina.rb CHANGED
@@ -1,8 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'jaina/version'
4
-
3
+ # @api public
4
+ # @since 0.1.0
5
5
  module Jaina
6
- class Error < StandardError; end
7
- # Your code goes here...
6
+ require_relative './jaina/version'
7
+ require_relative './jaina/parser'
8
+
9
+ # @since 0.1.0
10
+ extend Forwardable
11
+
12
+ TerminalExpr = Parser::Expression::Operator::Terminal
13
+ NonTerminalExpr = Parser::Expression::Operator::NonTerminal
14
+ GroupingExpr = Parser::Expression::Operator::Grouping
15
+
16
+ class << self
17
+ # @param program [String]
18
+ # @return [Jaina::Parser::AST]
19
+ #
20
+ # @api public
21
+ # @since 0.1.0
22
+ def parse(program)
23
+ Parser.parse(program)
24
+ end
25
+
26
+ # @param program [String]
27
+ # @return [Any]
28
+ #
29
+ # @api public
30
+ # @since 0.1.0
31
+ def evaluate(program)
32
+ parse(program).evaluate
33
+ end
34
+
35
+ # @param expression_klass [Class{Jaina::Parser::Expression::Operator::Abstract}]
36
+ # @return [void]
37
+ #
38
+ # @api public
39
+ # @since 0.1.0
40
+ def register_expression(expression_klass)
41
+ Jaina::Parser::Expression.register(expression_klass)
42
+ end
43
+
44
+ # @return [Array<String>]
45
+ #
46
+ # @api public
47
+ # @since 0.1.0
48
+ def expressions
49
+ Jaina::Parser::Expression.expressions
50
+ end
51
+
52
+ # @param expression_token [String]
53
+ # @return [Class{Jaina::Parser::Expressions::Operator::Abstract}]
54
+ #
55
+ # @api public
56
+ # @since 0.1.0
57
+ def fetch_expression(expression_token)
58
+ Jaina::Parser::Expression.fetch(expression_token)
59
+ end
60
+ alias_method :[], :fetch_expression
61
+ end
8
62
  end