Skip to content

Commit

Permalink
impl logical stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
omkarmoghe committed Mar 24, 2024
1 parent 9fba601 commit 82a1ed2
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 19 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## [Unreleased]

## [0.1.0] - 2024-03-23
## [0.1.0] - 2024-03-XX
- Initial release
57 changes: 48 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Expressive

A simple and flexible Ruby library to build and evaluate mathematical or other expressions.
A simple and flexible pure Ruby library for building and evaluating expressions.

## Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add expressive
`bundle add expressive`

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install expressive
`gem install expressive`

## Usage

Expand Down Expand Up @@ -48,6 +48,12 @@ Expression.new(:*, Variable.new("variable_a"), Scalar.new(2))
Expression.new(:+, Scalar.new(1), Scalar.new(2), output: "one_plus_two")
```

#### Special `operators`

Some operators, like logical `&&` and `||` are not methods in Ruby, so we pass a special string that Expressive understands.
- `&&` is represented by `:and`
- `||` is represented by `:or`

### Environment

The `Environment` holds state in the form of a `variables` hash and can evaluate `Expressions`, `Scalars`, and `Variables` within a context. The environment handles updates to the state as `Expressions` run.
Expand Down Expand Up @@ -75,7 +81,7 @@ environment.variables
#=> { "variable_a" => 1, "variable_b" => 2, "variable_c" => 3 }
```

When evaluating multiple objects at a time, the value of the _last_ object will be returned.
When evaluating multiple objects at a time, the value of the **last** object will be returned.

```ruby
environment = Environment.new
Expand All @@ -88,17 +94,50 @@ environment.evaluate(

### Serialization (to JSON)

All models support serialization via:
- `as_json`: builds a serializable hash representation of the object
- `to_json`: builds a JSON string representing the object
All models including the `Environment` support serialization via:
- `as_json`: builds a serializable `Hash` representation of the object
- `to_json`: builds a JSON `String` representing the object

### Building (from JSON)

<!-- TODO -->
To parse a JSON string, use the `Expressive.from_json` method.

```ruby
json_string = <<~JSON
{
"object": "Expressive::Variable",
"name": "score_a"
}
JSON
Expressive.from_json(json_string)
#=> <Variable...>
```

### Beyond math

<!-- TODO -->
`Scalars` and `Variables` can hold any type of value that's JSON serializable. This allows for more complex use cases such as:

#### Logical statements

```ruby
# variable_a > variable_b && variable_c
a_greater_than_b = Expression.new(
:>,
Variable.new("variable_a"),
Variable.new("variable_b"),
)
conditional = Expression.new(
:and,
a_greater_than_b,
Variable.new("variable_c"),
)
Environment.new(
"variable_a" => 2,
"variable_b" => 1,
"variable_c" => "truthy",
).evaluate(conditional)
#=> true
```

## Development

Expand Down
2 changes: 1 addition & 1 deletion expressive.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
spec.authors = ["Omkar Moghe"]
spec.email = ["theomkarmoghe@gmail.com"]

spec.summary = "A simple and flexible Ruby library to build and evaluate mathematical or other expressions."
spec.summary = "A simple and flexible pure Ruby library for building and evaluating expressions."
spec.homepage = "https://github.com/omkarmoghe/expressive"
spec.license = "MIT"
spec.required_ruby_version = ">= 3.0.0"
Expand Down
1 change: 1 addition & 0 deletions lib/expressive.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative "expressive/version"
require_relative "expressive/modules/serializable"
require_relative "expressive/evaluator"
require_relative "expressive/scalar"
require_relative "expressive/variable"
require_relative "expressive/expression"
Expand Down
4 changes: 2 additions & 2 deletions lib/expressive/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def evaluate_one(object)
raise MissingVariableError, "Environment missing variable #{key}."
end
when Expression
value = object.operands.map { |operand| evaluate(operand) }
.reduce(object.operator)
value = object.operands.map { |operand| Evaluator.new(evaluate(operand)) }
.reduce(object.operator)

if object.output
variables[object.output] = value
Expand Down
13 changes: 13 additions & 0 deletions lib/expressive/evaluator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'delegate'

module Expressive
class Evaluator < SimpleDelegator
def and(other)
__getobj__ && other.__getobj__
end

def or(other)
__getobj__ || other.__getobj__
end
end
end
28 changes: 28 additions & 0 deletions test/correctness/test_logical_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class TestLogicalMethods < Minitest::Test
def setup
@env = Expressive::Environment.new(
"variable_true" => true,
"variable_false" => false
)
end

def test_logical_and
expression = Expressive::Expression.new(
:and,
Expressive::Variable.new("variable_true"),
Expressive::Variable.new("variable_false")
)

assert_equal(false, @env.evaluate(expression))
end

def test_logical_or
expression = Expressive::Expression.new(
:or,
Expressive::Variable.new("variable_true"),
Expressive::Variable.new("variable_false")
)

assert(@env.evaluate(expression))
end
end
9 changes: 3 additions & 6 deletions test/correctness/test_parsing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ def test_variable
json = <<~JSON
{
"object": "Expressive::Variable",
"name": "score_a",
"value": 0.5
"name": "score_a"
}
JSON

Expand Down Expand Up @@ -34,13 +33,11 @@ def test_expression
"operands": [
{
"object": "Expressive::Variable",
"name": "rating_b",
"value": 1200
"name": "rating_b"
},
{
"object": "Expressive::Variable",
"name": "rating_a",
"value": 1000
"name": "rating_a"
}
]
}
Expand Down

0 comments on commit 82a1ed2

Please sign in to comment.