Rename project mm2ep_depend to namarara

This commit is contained in:
Glenn Y. Rolland 2019-12-21 17:15:14 +01:00
parent e0498abb05
commit c6c2e90e7f
25 changed files with 402 additions and 414 deletions

View File

@ -11,7 +11,7 @@ require: rubocop-rspec
# Allow bigger test cases ('examples')
RSpec/ExampleLength:
Enabled: true
Max: 8
Max: 10
Metrics/BlockLength:
Enabled: false

View File

@ -2,5 +2,5 @@ source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
# Specify your gem's dependencies in mm2ep_depend.gemspec
# Specify your gem's dependencies in namarara.gemspec
gemspec

View File

@ -16,7 +16,7 @@ and computes a boolean result from this AST.
Add this line to your application's Gemfile:
```ruby
gem 'mm2ep_depend'
gem 'namarara'
```
And then execute:
@ -25,7 +25,7 @@ And then execute:
Or install it yourself as:
$ gem install mm2ep_depend
$ gem install namarara
## Usage
@ -33,7 +33,7 @@ Or install it yourself as:
```
# Initialize Namarara
namarara = Namarara.new
namarara = Namarara::Parser.new(Namarara::Lexer.new)
# Build the binary expression tree (BET)
namarara_bet = namarara.parse('this AND (that OR other) AND something_else')
@ -58,7 +58,7 @@ namarara = Namarara::Parser.new(Namarara::Lexer.new)
# A set of rules i want to check
rules = [
{name: 'vulnetable_person', expr: 'is_adult AND is_subordinate'},
{name: 'vulnerable_person', expr: 'is_adult AND is_subordinate'},
{name: 'has_constraints', expr: 'is_adult AND has_children' },
{name: 'is_child', expr: 'NOT is_adult'}
# ...
@ -86,8 +86,7 @@ puts warnings.join("\n")
else
puts "Rien à dire :-)"
end
TODO: Write usage instructions here
```
## Development
@ -97,7 +96,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/mm2ep_depend.
Bug reports and pull requests are welcome on GitHub at https://github.com/glenux/namarara
## License

View File

@ -1,7 +1,7 @@
#!/usr/bin/env ruby
require "bundler/setup"
require "mm2ep_depend"
require "namarara"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

View File

@ -1,9 +1,9 @@
$:.insert(0, 'lib')
require 'mm2ep_depend'
require 'namarara'
def verify_input
parser = Mm2ep::Depend::Parser.new(Mm2ep::Depend::Lexer.new)
parser = Namarara::Parser.new(Namarara::Lexer.new)
# on démarre avec zéro alertes
warnings = []

View File

@ -5,45 +5,42 @@ $:.insert(0, 'lib')
require 'thor'
require 'rly'
require 'pry'
require 'mm2ep_depend'
require 'namarara'
require 'pp'
require 'logger'
module Mm2ep
module Depend
class ParseCli < Thor
desc 'parse OPTIONS INFILE VARS', 'Parse INFILE into tokens and evaluate VARS'
method_option :logfile, :type => :string, :aliases => '-l', :default => '-', :desc => "Logger with logfile"
def parse(infile, *vars)
unless options[:logfile].eql? '-'
logger = Logger.new("#{options[:logfile]}.log")
else
logger = Logger.new(STDOUT)
end
line = File.read(infile).gsub(/\n/,'')
lexer = Lexer.new(logger)
parser = Parser.new(lexer)
tab_vars = {}
vars.each do |var|
tab_vars[var.split('=')[0]] = var.split('=')[1]
end
# Give vars name and value from shell command to parser
parser.names=tab_vars
token = parser.parse(line.chomp, true)
pp token
puts "RAW : #{line}"
puts "EVAL: #{token.to_s}"
parser.check_grammar line, token
exit 1 unless !token.nil? && token.errors.empty?
puts "RESULT: #{token.compute}"
module Namarara
class ParseCli < Thor
desc 'parse OPTIONS INFILE VARS', 'Parse INFILE into tokens and evaluate VARS'
method_option :logfile, :type => :string, :aliases => '-l', :default => '-', :desc => "Logger with logfile"
def parse(infile, *vars)
unless options[:logfile].eql? '-'
logger = Logger.new("#{options[:logfile]}.log")
else
logger = Logger.new(STDOUT)
end
line = File.read(infile).gsub(/\n/,'')
lexer = Lexer.new(logger)
parser = Parser.new(lexer)
tab_vars = {}
vars.each do |var|
tab_vars[var.split('=')[0]] = var.split('=')[1]
end
# Give vars name and value from shell command to parser
parser.names=tab_vars
token = parser.parse(line.chomp, true)
pp token
puts "RAW : #{line}"
puts "EVAL: #{token.to_s}"
parser.check_grammar line, token
exit 1 unless !token.nil? && token.errors.empty?
puts "RESULT: #{token.compute}"
end
end
end
Mm2ep::Depend::ParseCli.start(ARGV)
Namarara::ParseCli.start(ARGV)

View File

@ -1,6 +0,0 @@
module Mm2ep
module Depend
class InvalidGrammar < Depend::EvalError
end
end
end

View File

@ -1,6 +0,0 @@
module Mm2ep
module Depend
class VarNotDefined < Depend::EvalError
end
end
end

View File

@ -1,13 +0,0 @@
require 'mm2ep_depend/version'
module Mm2epDepend
# Your code goes here...
end
require 'rly'
require 'mm2ep_depend/lexer'
require 'mm2ep_depend/parser'
require 'mm2ep_depend/eval_error'
require 'errors/var_not_defined'
require 'errors/invalid_grammar'

View File

@ -1,17 +0,0 @@
module Mm2ep
module Depend
class EvalError
attr_accessor :var
attr_accessor :message
def initialize(hash)
# validate input
raise ArgumentError unless hash[:message]
# load input
@message = hash[:message]
@var = hash[:var]
end
end
end
end

View File

@ -1,39 +0,0 @@
module Mm2ep
module Depend
class Lexer < Rly::Lex
attr_reader :logger
ignore "\t\n "
# token :SPACE, /\s+/
token :L_PAR, /\(/
token :R_PAR, /\)/
token :NUMBER, /[0-9]+(\.[0-9]+)?/
token :STRING, /"([^"]*)"/ do |s|
s.value.gsub!(/"(.*)"/, '\1')
s
end
token :EQ_OP, /\=/
token :T_BOOL, /true/i
token :F_BOOL, /false/i
token :VAR, /[a-z][a-zA-Z0-9_]+/
token :AND_OP, /AND/
token :OR_OP, /OR/
token :NOT_OP, /NOT/
def initialize(logger = nil)
@logger = logger
super()
end
on_error do |t|
unless t.lexer.logger.nil?
t.lexer.logger.error "Illegal character #{t.value}"
end
t.lexer.pos += 1
nil
end
end # class
end # module
end # module

View File

@ -1,258 +0,0 @@
module Mm2ep
module Depend
class TreeExpr
def compute
raise NotImplementedError
end
end
class TreeValue
attr_reader :value
def to_s
raise NotImplementedError
end
end
class VarValue < TreeValue
attr_reader :errors
def initialize(str, value)
@errors = []
@name = str
@value = value
@value = true if value =~ /^true$/i
@value = false if value =~ /^false$/i
return unless @value.nil?
@errors << VarNotDefined.new(
message: "No value for #{@name}",
var: @name
)
end
def compute
@value
end
def to_s
"var:#{@name}<-(#{@value})"
end
end
class NumberValue < TreeValue
attr_reader :errors
def initialize(str)
@errors = []
@value = str
end
def compute
@value
end
def to_s
"number:#{@value}"
end
end
class StringValue < TreeValue
attr_reader :errors
def initialize(str)
@errors = []
@value = str
end
def compute
@value
end
def to_s
"string:\"#{@value}\""
end
end
class BoolValue < TreeValue
attr_reader :errors
def initialize(str)
@errors = []
@value = case str
when /true/i then true
when /false/i then false
end
end
def compute
@value
end
def to_s
"bool:#{@value}"
end
end
class AndOp < TreeExpr
attr_reader :errors
def initialize(lval, rval)
@errors = []
@errors.concat lval.errors
@errors.concat rval.errors
@lval = lval
@rval = rval
end
def compute
@lval.compute && @rval.compute
end
def to_s
"( #{@lval} ) AND ( #{@rval} )"
end
end
class OrOp
attr_reader :errors
def initialize(lval, rval)
@errors = []
@errors.concat lval.errors
@errors.concat rval.errors
@lval = lval
@rval = rval
end
def compute
@lval.compute || @rval.compute
end
def to_s
"( #{@lval} ) OR ( #{@rval} )"
end
end
class NotOp
attr_reader :errors
def initialize(expr)
@errors = []
@errors.concat expr.errors
@expr = expr
end
def compute
!@expr.compute
end
def to_s
"NOT ( #{@expr} )"
end
end
class EqOp
attr_reader :errors
def initialize(lval, rval)
@errors = []
@errors.concat lval.errors
@errors.concat rval.errors
@lval = lval
@rval = rval
end
def compute
@lval.value == @rval.value
end
def to_s
"#{@lval} = #{@rval}"
end
end
# Cut HERE
# 8< ---- 8< ---- ...
class Parser < Rly::Yacc
attr_writer :names
# Check if grammar is valid
def check_grammar(line, tokens)
grammar = tokens.to_s.split(/=|AND|OR/)
expr = line.split(/=|AND|OR/)
return if grammar.size == expr.size
return if grammar.empty?
tokens.errors << InvalidGrammar.new(
message: 'Invalid Grammar'
)
end
precedence :left, :OR_OP
precedence :left, :AND_OP
precedence :left, :EQ_OP
precedence :right, :L_PAR, :R_PAR
precedence :right, :UMINUS
rule 'statement : expr' do |st, e|
st.value = e.value
end
rule 'expr : VAR' do |ex, l|
ex.value = EqOp.new(
VarValue.new(l.value, @names[l.value]),
BoolValue.new('true')
)
end
rule 'bool_expr : F_BOOL' do |ex, l|
ex.value = l.value
end
rule 'bool_expr : T_BOOL' do |ex, l|
ex.value = l.value
end
rule 'expr : bool_expr' do |ex, l|
ex.value = BoolValue.new(l.value.to_s)
end
rule 'expr : expr OR_OP expr' do |ex, l, _e, r|
ex.value = OrOp.new(l.value, r.value)
end
rule 'expr : expr AND_OP expr' do |ex, l, _e, r|
ex.value = AndOp.new(l.value, r.value)
end
rule 'expr : L_PAR expr R_PAR' do |ex, _l, e, _r|
ex.value = e.value
end
rule 'expr : NOT_OP expr %prec UMINUS' do |ex, _l, e|
ex.value = NotOp.new(e.value)
end
rule 'expr : VAR EQ_OP bool_expr' do |ex, v, _eq, n|
ex.value = EqOp.new(
VarValue.new(v.value.to_s, @names[v.value]),
BoolValue.new(n.value)
)
end
rule 'expr : VAR EQ_OP STRING' do |ex, v, _eq, n|
ex.value = EqOp.new(
VarValue.new(v.value.to_s, @names[v.value]),
StringValue.new(n.value)
)
end
rule 'expr : VAR EQ_OP NUMBER' do |ex, v, _eq, n|
ex.value = EqOp.new(
VarValue.new(v.value.to_s, @names[v.value]),
NumberValue.new(n.value)
)
end
end # class
end # module
end # module

11
lib/namarara.rb Normal file
View File

@ -0,0 +1,11 @@
require 'namarara/version'
module Namarara; end
require 'rly'
require 'namarara/lexer'
require 'namarara/parser'
require 'namarara/eval_error'
require 'namarara/errors/var_not_defined'
require 'namarara/errors/invalid_grammar'

View File

@ -0,0 +1,6 @@
module Namarara
module Errors
class InvalidGrammar < EvalError
end
end
end

View File

@ -0,0 +1,6 @@
module Namarara
module Errors
class VarNotDefined < EvalError
end
end
end

View File

@ -0,0 +1,15 @@
module Namarara
class EvalError
attr_accessor :var
attr_accessor :message
def initialize(hash)
# validate input
raise ArgumentError unless hash[:message]
# load input
@message = hash[:message]
@var = hash[:var]
end
end
end

37
lib/namarara/lexer.rb Executable file
View File

@ -0,0 +1,37 @@
module Namarara
class Lexer < Rly::Lex
attr_reader :logger
ignore "\t\n "
# token :SPACE, /\s+/
token :L_PAR, /\(/
token :R_PAR, /\)/
token :NUMBER, /[0-9]+(\.[0-9]+)?/
token :STRING, /"([^"]*)"/ do |s|
s.value.gsub!(/"(.*)"/, '\1')
s
end
token :EQ_OP, /\=/
token :T_BOOL, /true/i
token :F_BOOL, /false/i
token :VAR, /[a-z][a-zA-Z0-9_]+/
token :AND_OP, /AND/
token :OR_OP, /OR/
token :NOT_OP, /NOT/
def initialize(logger = nil)
@logger = logger
super()
end
on_error do |t|
unless t.lexer.logger.nil?
t.lexer.logger.error "Illegal character #{t.value}"
end
t.lexer.pos += 1
nil
end
end # class
end # module

256
lib/namarara/parser.rb Executable file
View File

@ -0,0 +1,256 @@
module Namarara
class TreeExpr
def compute
raise NotImplementedError
end
end
class TreeValue
attr_reader :value
def to_s
raise NotImplementedError
end
end
class VarValue < TreeValue
attr_reader :errors
def initialize(str, value)
@errors = []
@name = str
@value = value
@value = true if value =~ /^true$/i
@value = false if value =~ /^false$/i
return unless @value.nil?
@errors << Errors::VarNotDefined.new(
message: "No value for #{@name}",
var: @name
)
end
def compute
@value
end
def to_s
"var:#{@name}<-(#{@value})"
end
end
class NumberValue < TreeValue
attr_reader :errors
def initialize(str)
@errors = []
@value = str
end
def compute
@value
end
def to_s
"number:#{@value}"
end
end
class StringValue < TreeValue
attr_reader :errors
def initialize(str)
@errors = []
@value = str
end
def compute
@value
end
def to_s
"string:\"#{@value}\""
end
end
class BoolValue < TreeValue
attr_reader :errors
def initialize(str)
@errors = []
@value = case str
when /true/i then true
when /false/i then false
end
end
def compute
@value
end
def to_s
"bool:#{@value}"
end
end
class AndOp < TreeExpr
attr_reader :errors
def initialize(lval, rval)
@errors = []
@errors.concat lval.errors
@errors.concat rval.errors
@lval = lval
@rval = rval
end
def compute
@lval.compute && @rval.compute
end
def to_s
"( #{@lval} ) AND ( #{@rval} )"
end
end
class OrOp
attr_reader :errors
def initialize(lval, rval)
@errors = []
@errors.concat lval.errors
@errors.concat rval.errors
@lval = lval
@rval = rval
end
def compute
@lval.compute || @rval.compute
end
def to_s
"( #{@lval} ) OR ( #{@rval} )"
end
end
class NotOp
attr_reader :errors
def initialize(expr)
@errors = []
@errors.concat expr.errors
@expr = expr
end
def compute
!@expr.compute
end
def to_s
"NOT ( #{@expr} )"
end
end
class EqOp
attr_reader :errors
def initialize(lval, rval)
@errors = []
@errors.concat lval.errors
@errors.concat rval.errors
@lval = lval
@rval = rval
end
def compute
@lval.value == @rval.value
end
def to_s
"#{@lval} = #{@rval}"
end
end
# Cut HERE
# 8< ---- 8< ---- ...
class Parser < Rly::Yacc
attr_writer :names
# Check if grammar is valid
def check_grammar(line, tokens)
grammar = tokens.to_s.split(/=|AND|OR/)
expr = line.split(/=|AND|OR/)
return if grammar.size == expr.size
return if grammar.empty?
tokens.errors << Errors::InvalidGrammar.new(
message: 'Invalid Grammar'
)
end
precedence :left, :OR_OP
precedence :left, :AND_OP
precedence :left, :EQ_OP
precedence :right, :L_PAR, :R_PAR
precedence :right, :UMINUS
rule 'statement : expr' do |st, e|
st.value = e.value
end
rule 'expr : VAR' do |ex, l|
ex.value = EqOp.new(
VarValue.new(l.value, @names[l.value]),
BoolValue.new('true')
)
end
rule 'bool_expr : F_BOOL' do |ex, l|
ex.value = l.value
end
rule 'bool_expr : T_BOOL' do |ex, l|
ex.value = l.value
end
rule 'expr : bool_expr' do |ex, l|
ex.value = BoolValue.new(l.value.to_s)
end
rule 'expr : expr OR_OP expr' do |ex, l, _e, r|
ex.value = OrOp.new(l.value, r.value)
end
rule 'expr : expr AND_OP expr' do |ex, l, _e, r|
ex.value = AndOp.new(l.value, r.value)
end
rule 'expr : L_PAR expr R_PAR' do |ex, _l, e, _r|
ex.value = e.value
end
rule 'expr : NOT_OP expr %prec UMINUS' do |ex, _l, e|
ex.value = NotOp.new(e.value)
end
rule 'expr : VAR EQ_OP bool_expr' do |ex, v, _eq, n|
ex.value = EqOp.new(
VarValue.new(v.value.to_s, @names[v.value]),
BoolValue.new(n.value)
)
end
rule 'expr : VAR EQ_OP STRING' do |ex, v, _eq, n|
ex.value = EqOp.new(
VarValue.new(v.value.to_s, @names[v.value]),
StringValue.new(n.value)
)
end
rule 'expr : VAR EQ_OP NUMBER' do |ex, v, _eq, n|
ex.value = EqOp.new(
VarValue.new(v.value.to_s, @names[v.value]),
NumberValue.new(n.value)
)
end
end # class
end # module

View File

@ -1,3 +1,3 @@
module Mm2epDepend
module Namarara
VERSION = '0.9.4'.freeze
end

View File

@ -1,16 +1,16 @@
# coding: utf-8
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'mm2ep_depend/version'
require 'namarara/version'
Gem::Specification.new do |spec|
spec.name = 'mm2ep_depend'
spec.version = Mm2epDepend::VERSION
spec.authors = ['Roguelearg']
spec.email = ["torre.brendon@gmail.com"]
spec.name = 'namarara'
spec.version = Namarara::VERSION
spec.authors = ['Brendon Torre', 'Glenn Y. Rolland']
spec.email = ['glenux@glenux.net']
spec.summary = %q{A library and tools for expressions}
spec.description = %q{A library and tools for expressions}
spec.summary = %q{A library and tools for parsing boolean expressions}
spec.description = %q{A library and tools for parsing boolean expressions}
spec.homepage = 'https://datatransition.net'
spec.license = 'MIT'
@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", "~> 1.15"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "opal"
# spec.add_development_dependency "opal"
spec.add_development_dependency "pry"
spec.add_development_dependency "rubocop"
spec.add_development_dependency "rubocop-rspec"

View File

@ -1,9 +1,9 @@
require 'spec_helper'
require 'mm2ep_depend'
require 'namarara'
describe Mm2ep::Depend::Lexer do
describe Namarara::Lexer do
let(:lexer) do
Mm2ep::Depend::Lexer.new
Namarara::Lexer.new
end
it 'has to recognize AND operator' do

View File

@ -1,18 +1,18 @@
require 'spec_helper'
require 'mm2ep_depend'
require 'namarara'
describe Mm2ep::Depend::Parser do
describe Namarara::Parser do
let(:parser) do
Mm2ep::Depend::Parser.new(
Mm2ep::Depend::Lexer.new
)
Namarara::Parser.new(Namarara::Lexer.new)
end
it 'has to report var which is not defined' do
line = 'character = true'
parser.names = {}
token = parser.parse(line)
errors = token.errors.select { |el| el.is_a? Mm2ep::Depend::VarNotDefined }
errors = token.errors.select do |el|
el.is_a? Namarara::Errors::VarNotDefined
end
errors.size.must_equal 1
errors[0].var.must_equal 'character'
end
@ -21,7 +21,9 @@ describe Mm2ep::Depend::Parser do
line = 'a_girl_has_no_name AND character'
parser.names = {}
token = parser.parse(line)
errors = token.errors.select { |el| el.is_a? Mm2ep::Depend::VarNotDefined }
errors = token.errors.select do |el|
el.is_a? Namarara::Errors::VarNotDefined
end
errors.size.must_equal 2
errors[0].var.must_equal 'a_girl_has_no_name'
errors[1].var.must_equal 'character'
@ -34,7 +36,7 @@ describe Mm2ep::Depend::Parser do
token = parser.parse(line)
parser.check_grammar line, token
token.errors.select do |elem|
elem.is_a? Mm2ep::Depend::InvalidGrammar
elem.is_a? Namarara::Errors::InvalidGrammar
end.size.must_equal 1
end

View File

@ -1,9 +1,9 @@
require 'spec_helper'
require 'mm2ep_depend'
require 'namarara'
describe Mm2ep::Depend::Parser do
describe Namarara::Parser do
let(:parser) do
Mm2ep::Depend::Parser.new(Mm2ep::Depend::Lexer.new)
Namarara::Parser.new(Namarara::Lexer.new)
end
it 'has to do not before or' do

View File

@ -1,11 +1,9 @@
require 'spec_helper'
require 'mm2ep_depend'
require 'namarara'
describe Mm2ep::Depend::Parser do
describe Namarara::Parser do
let(:parser) do
Mm2ep::Depend::Parser.new(
Mm2ep::Depend::Lexer.new
)
Namarara::Parser.new(Namarara::Lexer.new)
end
it 'has to find var and compute it to expr' do

View File

@ -1,5 +1,5 @@
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require "mm2ep_depend"
require "namarara"
require "minitest/autorun"
require 'pathname'