Skip to content

Commit

Permalink
Merge f03678e into e1e2049
Browse files Browse the repository at this point in the history
  • Loading branch information
0exp committed Jun 24, 2019
2 parents e1e2049 + f03678e commit 60b8005
Show file tree
Hide file tree
Showing 13 changed files with 398 additions and 60 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog
All notable changes to this project will be documented in this file.

## [0.4.0] - 2019-06-24
### Added
- Support for operand attributes and operator attributes;

## [0.3.0] - 2019-06-21
### Added
- Support for initial context: `Jaina.evaluate(program, **initial_context)`
Expand Down
53 changes: 46 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,57 @@ context.get(:b) # => Jaina::Parser::AST::Contex::UndefinedContextKeyError
### Parse your code (build AST)

```ruby
# NOTE: without arguments
Jaina.parse('A AND B AND (C OR D) OR A AND (C OR E)')
# => #<Jaina::Parser::AST:0x00007fd6f424a2e8>

# NOTE: with arguments
Jaina.parse('A[1,2] AND B[3,4]')
# => #<Jaina::Parser::AST:0x00007fd6f424a2e9>
```

---

### Evaluate your code

```ruby
ast = Jaina.parse('A AND B AND (C OR D) OR A AND (C OR E)')
ast = Jaina.parse('A AND B[5,test] AND (C OR D) OR A AND (C OR E)')
ast.evaluate

# --- or ---
Jaina.evaluate('A AND B AND (C OR D) OR A AND (C OR E)')
Jaina.evaluate('A AND B[5,test] AND (C OR D) OR A AND (C OR E)')

# --- you can set initial context of your program ---
Jaina.evaluate('A AND B', login: 'admin', logged_in: true)
Jaina.evaluate('A AND B[5,test]', login: 'admin', logged_in: true)
```

---

### Custom operator/operand arguments

```ruby
# NOTE: use []
Jaina.parse('A[1,true] AND B[false,"false"]')

# NOTE:
# all your arguments will be typecasted to
# the concrete type inferred from the argument literal

Jaina.parse('A[1,true,false,"false"]') # 1, true, false "false"

# NOTE: access to the argument list
class A < Jaina::TerminalExpr
token 'A'

def evaluate(context)
# A[1,true,false,"false"]

arguments[0] # => 1
arguments[1] # => true
arguments[2] # => false
arguments[3] # => "false"
end
end
```

---
Expand Down Expand Up @@ -174,7 +208,9 @@ class InitState < Jaina::TerminalExpr
token 'INIT'

def evaluate(context)
context.set(:current_value, 0)
initial_value = arguments[0] || 0

context.set(:current_value, initial_value)
end
end

Expand All @@ -186,12 +222,15 @@ Jaina.register_expression(InitState)
# step 6: run your program

# NOTE: with initial context
Jaina.evaluate('CHECK AND ADD', current_value: -1) # => false
Jaina.evaluate('CHECK AND ADD', current_value: 2) # => 12
Jaina.evaluate('CHECK AND ADD', current_value: -1) # => 9
Jaina.evaluate('CHECK AND ADD', current_value: 2) # => false

# NOTE: without initial context
Jaina.evaluate('INIT AND ADD') # => 10
Jaina.evaluate('INIT AND (CHECK OR ADD)') # => 12
Jaina.evaluate('INIT AND (CHECK OR ADD)') # => 10

# NOTE: with arguments
Jaina.evaluate('INIT[100] AND ADD') => # 112
```

---
Expand Down
20 changes: 10 additions & 10 deletions lib/jaina/parser/ast/tree_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ def build
# @since 0.1.0
attr_reader :prefix_form

# @return [Array<String>]
# @return [Array<Jaina::Parser::Tokenizer::Token>]
#
# @api private
# @since 0.1.0
attr_reader :tokens

# @param token_series [Array<String>]
# @param token_series [Array<Jaina::Parser::Tokenizer::Token>]
# @return [Array<Jaina::Parser::Expression::Operator::Abstract>]
#
# @api private
Expand All @@ -80,14 +80,14 @@ def build_expression_tree(token_series)
return if current_token.nil?

case
when terminal?(current_token)
when terminal?(current_token.token)
build_terminal_expression(current_token)
when non_terminal?(current_token)
when non_terminal?(current_token.token)
build_non_terminal_expression(current_token, token_series)
end
end

# @param token_series [Array<String>]
# @param token_series [Array<Jaina::Parser::Tokenizer::Token>]
# @return [String, NilClass]
#
# @api private
Expand All @@ -96,7 +96,7 @@ def extract_second_token(token_series)
token_series.shift
end

# @param current_token [String]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @return [Jaina::Parser::Expression::Operator::Abstract]
#
# @api private
Expand All @@ -105,20 +105,20 @@ def build_terminal_expression(current_token)
Jaina::Parser::Expression.build(current_token)
end

# @param current_token [String]
# @param token_series [Array<String>]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @param token_series [Array<Jaina::Parser::Tokenizer::Token>]
# @return [Jaina::Parser::Expression::Operator::Abstract]
#
# @api private
# @since 0.1.0
def build_non_terminal_expression(current_token, token_series)
case
when acts_as_unary_term?(current_token)
when acts_as_unary_term?(current_token.token)
Jaina::Parser::Expression.build(
current_token,
build_expression_tree(token_series)
)
when acts_as_binary_term?(current_token)
when acts_as_binary_term?(current_token.token)
Jaina::Parser::Expression.build(
current_token,
build_expression_tree(token_series),
Expand Down
32 changes: 17 additions & 15 deletions lib/jaina/parser/code_converter/to_postfix_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def call
# @since 0.1.0
attr_reader :program

# @return [Array<String>]
# @return [Array<Jaina::Parser::Tokenizer::Token>]
#
# @api private
# @since 0.1.0
Expand All @@ -70,13 +70,13 @@ def to_postfix_form
current_token = token_series.shift

case
when non_terminal?(current_token)
when non_terminal?(current_token.token)
process_non_terminal_token(final_expression, structure_operators, current_token)
when group_opener?(current_token)
when group_opener?(current_token.token)
process_group_opening_token(final_expression, structure_operators, current_token)
when group_closener?(current_token)
when group_closener?(current_token.token)
process_group_closing_token(final_expression, structure_operators, current_token)
when terminal?(current_token)
when terminal?(current_token.token)
process_terminal_token(final_expression, structure_operators, current_token)
end
end
Expand All @@ -89,7 +89,7 @@ def to_postfix_form

# @param final_expression [Array<String>]
# @param structure_operators [Array<String>]
# @param current_token [String]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @return [void]
#
# @api private
Expand All @@ -98,12 +98,14 @@ def process_non_terminal_token(final_expression, structure_operators, current_to
if structure_operators.any? # NOTE: check assocaitivity with potential next token
potential_second_token = structure_operators.last

if non_terminal?(potential_second_token)
current_expression = Jaina::Parser::Expression.fetch(current_token)
next_expression = Jaina::Parser::Expression.fetch(potential_second_token)
if non_terminal?(potential_second_token.token)
current_expression = Jaina::Parser::Expression.fetch(current_token.token)
next_expression = Jaina::Parser::Expression.fetch(potential_second_token.token)

# NOTE: form infix to postfix form switching
final_expression.push(structure_operators.pop) if current_expression.lower?(next_expression)
if current_expression.lower?(next_expression)
final_expression.push(structure_operators.pop)
end
end
end

Expand All @@ -112,7 +114,7 @@ def process_non_terminal_token(final_expression, structure_operators, current_to

# @param final_expression [Array<String>]
# @param structure_operators [Array<String>]
# @param current_token [String]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @return [void]
#
# @api private
Expand All @@ -124,13 +126,13 @@ def process_group_opening_token(final_expression, structure_operators, current_t

# @param final_expression [Array<String>]
# @param structure_operators [Array<String>]
# @param current_token [String]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @return [void]
#
# @api private
# @since 0.1.0
def process_group_closing_token(final_expression, structure_operators, current_token)
until group_opener?(structure_operators.last)
until group_opener?(structure_operators.last.token)
# NOTE: push all tokens to the final expression
final_expression.push(structure_operators.pop)
end
Expand All @@ -141,7 +143,7 @@ def process_group_closing_token(final_expression, structure_operators, current_t

# @param final_expression [Array<String>]
# @param structure_operators [Array<String>]
# @param current_token [String]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @return [void]
#
# @api private
Expand All @@ -153,7 +155,7 @@ def process_terminal_token(final_expression, structure_operators, current_token)

# @param final_expression [Array<String>]
# @param structure_operators [Array<String>]
# @param current_token [String]
# @param current_token [Jaina::Parser::Tokenizer::Token]
# @return [void]
#
# @api private
Expand Down
20 changes: 10 additions & 10 deletions lib/jaina/parser/code_converter/to_prefix_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def call
# @since 0.1.0
attr_reader :postfix_form

# @return [Array<String>]
# @return [Array<Jaina::Parser::Tokenizer::Token>]
#
# @api private
# @since 0.1.0
Expand All @@ -71,15 +71,15 @@ def to_prefix_form
current_token = token_series.shift

case
when terminal?(current_token)
when terminal?(current_token.token)
process_terminal_token(current_token, expression_stack)
when non_terminal?(current_token)
when non_terminal?(current_token.token)
process_non_terminal_token(current_token, expression_stack)
end
end

# NOTE: build prefixed program string
Jaina::Parser::Tokenizer.join(expression_stack)
Jaina::Parser::Tokenizer.raw_join(expression_stack)
end

# @param current_token [String]
Expand All @@ -89,7 +89,7 @@ def to_prefix_form
# @api private
# @since 0.1.0
def process_terminal_token(current_token, expression_stack)
expression_stack.push(current_token)
expression_stack.push(current_token.raw_token)
end

# @param current_token [String]
Expand All @@ -99,22 +99,22 @@ def process_terminal_token(current_token, expression_stack)
# @api private
# @since 0.1.0
def process_non_terminal_token(current_token, expression_stack)
expression = Jaina::Parser::Expression.fetch(current_token)
expression = Jaina::Parser::Expression.fetch(current_token.token)

case # TODO: dry
when expression.acts_as_binary_term?
first_operand = expression_stack.pop
second_operand = expression_stack.pop

prefixed_expression_parts = [expression.token, second_operand, first_operand]
prefixed_expression = Jaina::Parser::Tokenizer.join(prefixed_expression_parts)
prefixed_expression_parts = [current_token.raw_token, second_operand, first_operand]
prefixed_expression = Jaina::Parser::Tokenizer.raw_join(prefixed_expression_parts)

expression_stack.push(prefixed_expression)
when expression.acts_as_unary_term?
operand = expression_stack.pop

prefixed_expression_parts = [expression.token, operand]
prefixed_expression = Jaina::Parser::Tokenizer.join(prefixed_expression_parts)
prefixed_expression_parts = [current_token.raw_token, operand]
prefixed_expression = Jaina::Parser::Tokenizer.raw_join(prefixed_expression_parts)

expression_stack.push(prefixed_expression)
end
Expand Down
10 changes: 6 additions & 4 deletions lib/jaina/parser/expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ module Jaina::Parser::Expression
register(Operator::RightCorner)

class << self
# @param expression_token [String]
# @param expression_token [Jaina::Parser::Tokenizer::Token]
# @param arguments [Array<Any>]
# @return [Jaina::Parser::Expression::Operator::Abstract]
#
# @api private
# @since 0.1.0
def build(expression_token, *arguments)
expression = fetch(expression_token)
arguments.any? ? expression.new(*arguments) : expression.new
def build(expression_token, *expression_arguments)
expression = fetch(expression_token.token)
expression.new(arguments: expression_token.arguments, expressions: expression_arguments)
end

# @param expression_token [String]
Expand Down Expand Up @@ -82,6 +82,8 @@ def acts_as_binary_term?(expression_token)
# @since 0.1.0
def acts_as_unary_term?(expression_token)
fetch(expression_token).acts_as_unary_term?
rescue => error
binding.pry
end
end
end
12 changes: 10 additions & 2 deletions lib/jaina/parser/expression/operator/abstract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,26 @@ def lower?(another_operator)
end
end

# @return [Array<Any>]
#
# @api private
# @since 0.4.0
attr_reader :arguments

# @return [Array<Jaina::Parser::Expressions::Operator::Abstract>]
#
# @api private
# @since 0.1.0
attr_reader :expressions

# @param expressions [Array<Jaina::Parser::Expression::Operator::Abstract>]
# @option arguments [Array<Any>]
# @option expressions [Array<Jaina::Parser::Expression::Operator::Abstract>]
# @return [void]
#
# @api private
# @since 0.1.0
def initialize(*expressions)
def initialize(arguments: [], expressions: [])
@arguments = arguments
@expressions = expressions
end

Expand Down
Loading

0 comments on commit 60b8005

Please sign in to comment.