jaina 0.0.0 → 0.1.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.
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