formula_dsl 0.1.1 → 0.2.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.
- data/Gemfile +6 -3
- data/Gemfile.lock +2 -2
- data/VERSION +1 -1
- data/formula_dsl.gemspec +24 -12
- data/lib/formula_dsl/binary_expression.rb +26 -0
- data/lib/formula_dsl/binary_expression_factory.rb +22 -0
- data/lib/formula_dsl/binary_operations.rb +12 -0
- data/lib/formula_dsl/function_expression.rb +22 -0
- data/lib/formula_dsl/function_expression_factory.rb +31 -0
- data/lib/formula_dsl/functions.rb +9 -0
- data/lib/formula_dsl/parser.rb +92 -0
- data/lib/formula_dsl/transformer.rb +65 -0
- data/lib/formula_dsl.rb +8 -1
- data/spec/formula_dsl/binary_expression_factory_spec.rb +31 -0
- data/spec/formula_dsl/binary_expression_spec.rb +18 -0
- data/spec/formula_dsl/function_expression_factory_spec.rb +28 -0
- data/spec/formula_dsl/function_expression_spec.rb +17 -0
- data/spec/formula_dsl/parser_spec.rb +87 -0
- data/spec/formula_dsl/transformer_spec.rb +60 -0
- data/spec/spec_helper.rb +1 -0
- metadata +55 -23
- data/lib/formula_dsl/formula_parser.rb +0 -90
- data/spec/formula_dsl/formula_parser_spec.rb +0 -80
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -12,7 +12,7 @@ GEM
|
|
12
12
|
json (1.6.5)
|
13
13
|
parslet (1.3.0)
|
14
14
|
blankslate (~> 2.0)
|
15
|
-
rake (0.9.2)
|
15
|
+
rake (0.9.2.2)
|
16
16
|
rdoc (3.12)
|
17
17
|
json (~> 1.4)
|
18
18
|
rspec (2.9.0)
|
@@ -30,5 +30,5 @@ PLATFORMS
|
|
30
30
|
DEPENDENCIES
|
31
31
|
jeweler (~> 1.8.3)
|
32
32
|
parslet (~> 1.3.0)
|
33
|
-
rake (
|
33
|
+
rake (> 0.9.2)
|
34
34
|
rspec (~> 2.9.0)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/formula_dsl.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "formula_dsl"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Sergio Junior"]
|
12
|
-
s.date = "2012-03
|
12
|
+
s.date = "2012-04-03"
|
13
13
|
s.description = " External DSL to express math operations like you do in MSExcel formula bar. The parser generate a AST (Hash) for a given expression. "
|
14
14
|
s.email = "sergior.jr@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -28,14 +28,26 @@ Gem::Specification.new do |s|
|
|
28
28
|
"VERSION",
|
29
29
|
"formula_dsl.gemspec",
|
30
30
|
"lib/formula_dsl.rb",
|
31
|
-
"lib/formula_dsl/
|
32
|
-
"
|
31
|
+
"lib/formula_dsl/binary_expression.rb",
|
32
|
+
"lib/formula_dsl/binary_expression_factory.rb",
|
33
|
+
"lib/formula_dsl/binary_operations.rb",
|
34
|
+
"lib/formula_dsl/function_expression.rb",
|
35
|
+
"lib/formula_dsl/function_expression_factory.rb",
|
36
|
+
"lib/formula_dsl/functions.rb",
|
37
|
+
"lib/formula_dsl/parser.rb",
|
38
|
+
"lib/formula_dsl/transformer.rb",
|
39
|
+
"spec/formula_dsl/binary_expression_factory_spec.rb",
|
40
|
+
"spec/formula_dsl/binary_expression_spec.rb",
|
41
|
+
"spec/formula_dsl/function_expression_factory_spec.rb",
|
42
|
+
"spec/formula_dsl/function_expression_spec.rb",
|
43
|
+
"spec/formula_dsl/parser_spec.rb",
|
44
|
+
"spec/formula_dsl/transformer_spec.rb",
|
33
45
|
"spec/spec_helper.rb"
|
34
46
|
]
|
35
47
|
s.homepage = "http://github.com/sergiojunior/formula_dsl"
|
36
48
|
s.licenses = ["MIT"]
|
37
49
|
s.require_paths = ["lib"]
|
38
|
-
s.rubygems_version = "1.8.
|
50
|
+
s.rubygems_version = "1.8.21"
|
39
51
|
s.summary = "External DSL that creates AST representation (Hash) for a given mathematical expression."
|
40
52
|
|
41
53
|
if s.respond_to? :specification_version then
|
@@ -43,20 +55,20 @@ Gem::Specification.new do |s|
|
|
43
55
|
|
44
56
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
57
|
s.add_runtime_dependency(%q<parslet>, ["~> 1.3.0"])
|
46
|
-
s.
|
47
|
-
s.
|
48
|
-
s.
|
58
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.9.0"])
|
59
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
60
|
+
s.add_development_dependency(%q<rake>, ["> 0.9.2"])
|
49
61
|
else
|
50
62
|
s.add_dependency(%q<parslet>, ["~> 1.3.0"])
|
51
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
52
|
-
s.add_dependency(%q<rake>, ["~> 0.9.2"])
|
53
63
|
s.add_dependency(%q<rspec>, ["~> 2.9.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
65
|
+
s.add_dependency(%q<rake>, ["> 0.9.2"])
|
54
66
|
end
|
55
67
|
else
|
56
68
|
s.add_dependency(%q<parslet>, ["~> 1.3.0"])
|
57
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
58
|
-
s.add_dependency(%q<rake>, ["~> 0.9.2"])
|
59
69
|
s.add_dependency(%q<rspec>, ["~> 2.9.0"])
|
70
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
71
|
+
s.add_dependency(%q<rake>, ["> 0.9.2"])
|
60
72
|
end
|
61
73
|
end
|
62
74
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module FormulaDSL
|
3
|
+
|
4
|
+
class BinaryExpression
|
5
|
+
attr_reader :operator, :left_term, :right_term
|
6
|
+
|
7
|
+
def initialize(operator, left_term, right_term)
|
8
|
+
@operator = operator
|
9
|
+
@left_term = left_term
|
10
|
+
@right_term = right_term
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply
|
14
|
+
@left_term = @left_term.apply if @left_term.respond_to? :apply
|
15
|
+
@right_term = @right_term.apply if @right_term.respond_to? :apply
|
16
|
+
operation = BinaryExpressionFactory.new(@operator)
|
17
|
+
|
18
|
+
operation.call(@left_term, @right_term)
|
19
|
+
end
|
20
|
+
|
21
|
+
def == (other)
|
22
|
+
@operator == other.operator && @left_term == other.left_term && @right_term == other.right_term
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module FormulaDSL
|
4
|
+
class MissingBinaryOperationError < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
module BinaryExpressionFactory
|
8
|
+
|
9
|
+
def self.new(operator)
|
10
|
+
operation = case operator
|
11
|
+
when :+ then ::FormulaDSL::BinaryOperations::ADDITION
|
12
|
+
when :- then ::FormulaDSL::BinaryOperations::SUBTRACTION
|
13
|
+
when :* then ::FormulaDSL::BinaryOperations::MULTIPLICATION
|
14
|
+
when :/ then ::FormulaDSL::BinaryOperations::DIVISION
|
15
|
+
else raise MissingBinaryOperationError, "Sorry, but we don't have a implementation for the operator ( #{operator} )."
|
16
|
+
end
|
17
|
+
|
18
|
+
operation
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module FormulaDSL
|
4
|
+
module BinaryOperations
|
5
|
+
|
6
|
+
ADDITION = Proc.new{|left_term, right_term| "#{left_term} + #{right_term}" }
|
7
|
+
SUBTRACTION = Proc.new{|left_term, right_term| "#{left_term} - #{right_term}" }
|
8
|
+
DIVISION = Proc.new{|left_term, right_term| "#{left_term} / #{right_term}" }
|
9
|
+
MULTIPLICATION = Proc.new{|left_term, right_term| "#{left_term} * #{right_term}" }
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module FormulaDSL
|
3
|
+
|
4
|
+
class FunctionExpression
|
5
|
+
attr_reader :name, :args
|
6
|
+
def initialize(name, *args)
|
7
|
+
@name = name
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply
|
12
|
+
function = FunctionExpressionFactory.new(@name)
|
13
|
+
|
14
|
+
function.call(@args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def == (other)
|
18
|
+
@name == other.name && @args == other.args
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module FormulaDSL
|
4
|
+
class MissingFunctionError < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
module FunctionExpressionFactory
|
8
|
+
|
9
|
+
def self.new(function_name)
|
10
|
+
begin
|
11
|
+
function = constantize("FormulaDSL::Functions::#{function_name.upcase}")
|
12
|
+
|
13
|
+
function
|
14
|
+
rescue(NameError)
|
15
|
+
raise MissingFunctionError, "If you want to use the function #{function_name} you must implement that as a proc named FormulaDSL::Functions::#{function_name.upcase}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def self.constantize( string )
|
21
|
+
module_names = string.split('::')
|
22
|
+
constant = Module.const_get( module_names.first.to_sym )
|
23
|
+
|
24
|
+
module_names = module_names.drop(1)
|
25
|
+
module_names.each{ |name| constant = constant.const_get( name.to_sym ) }
|
26
|
+
|
27
|
+
constant
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module FormulaDSL
|
3
|
+
class Parser < Parslet::Parser
|
4
|
+
|
5
|
+
# Single char rules
|
6
|
+
rule(:lparen) { str('(') >> space? }
|
7
|
+
rule(:rparen) { str(')') >> space? }
|
8
|
+
rule(:comma) { str(',') >> space? }
|
9
|
+
|
10
|
+
# non significant character's
|
11
|
+
rule(:space) { match('\s').repeat(1) }
|
12
|
+
rule(:space?) { space.maybe }
|
13
|
+
|
14
|
+
# Literal's
|
15
|
+
rule(:number) { match('\d').repeat(1) }
|
16
|
+
rule(:quote) { match('"') }
|
17
|
+
rule(:string) { quote >> space? >> match('.') >> space? >> quote}
|
18
|
+
rule(:identifier) { match['\w'].repeat(1) }
|
19
|
+
|
20
|
+
# Argument
|
21
|
+
rule (:argument) { identifier | number }
|
22
|
+
rule (:arglist) { (argument >> comma.maybe).repeat }
|
23
|
+
|
24
|
+
# Operators
|
25
|
+
rule(:add_operator ) { match(['+']) }
|
26
|
+
rule(:sub_operator ) { match('-') }
|
27
|
+
rule(:mult_operator) { match(['*']) }
|
28
|
+
rule(:div_operator ) { match(['/']) }
|
29
|
+
|
30
|
+
rule :operator do
|
31
|
+
add_operator | sub_operator | mult_operator | div_operator
|
32
|
+
end
|
33
|
+
|
34
|
+
#binary operations + - / *
|
35
|
+
rule :addition do
|
36
|
+
( number.as(:left) >> space? >> add_operator >> space? >> ( number ).as(:right) ).as(:+)
|
37
|
+
end
|
38
|
+
|
39
|
+
rule :subtraction do
|
40
|
+
( number.as(:left) >> space? >> sub_operator >> space? >> ( number ).as(:right) ).as(:-)
|
41
|
+
end
|
42
|
+
|
43
|
+
rule :multiplication do
|
44
|
+
( number.as(:left) >> space? >> mult_operator >> space? >> ( number ).as(:right) ).as(:*)
|
45
|
+
end
|
46
|
+
|
47
|
+
rule :division do
|
48
|
+
( number.as(:left) >> space? >> div_operator >> space? >> ( number ).as(:right) ).as(:/)
|
49
|
+
end
|
50
|
+
|
51
|
+
rule :function do
|
52
|
+
(identifier.as(:name) >> lparen >> arglist.as(:args) >> rparen).as(:function)
|
53
|
+
end
|
54
|
+
|
55
|
+
rule :string_concat do
|
56
|
+
( string.as(:left) >> space? >> add_operator >> space? >> ( string ).as(:right) ).as(:+)
|
57
|
+
end
|
58
|
+
|
59
|
+
rule :single_expression do
|
60
|
+
function | addition | subtraction | multiplication | division | string_concat | number | string
|
61
|
+
end
|
62
|
+
|
63
|
+
# expression with other expression (compose expression)
|
64
|
+
# Ex: expression >> some_op >> number === [ Mont(data) - 1 ]
|
65
|
+
rule :subtraction_expression do
|
66
|
+
(single_expression.as(:left) >> space? >> sub_operator >> space? >> (single_expression | number).as(:right) ).as(:-)
|
67
|
+
end
|
68
|
+
|
69
|
+
rule :addition_expression do
|
70
|
+
(single_expression.as(:left) >> space? >> add_operator >> space? >> ( expression_list | number ).as(:right) ).as(:+)
|
71
|
+
end
|
72
|
+
|
73
|
+
rule :multiplication_expression do
|
74
|
+
(single_expression.as(:left) >> space? >> mult_operator >> space? >> (single_expression | number).as(:right) ).as(:*)
|
75
|
+
end
|
76
|
+
|
77
|
+
rule :division_expression do
|
78
|
+
(single_expression.as(:left) >> space? >> div_operator >> space? >> (single_expression | number).as(:right) ).as(:/)
|
79
|
+
end
|
80
|
+
|
81
|
+
rule :string_concat_expression do
|
82
|
+
(single_expression.as(:left) >> space? >> add_operator >> space? >> ( expression_list | string ).as(:right) ).as(:+)
|
83
|
+
end
|
84
|
+
|
85
|
+
rule :expression_list do
|
86
|
+
addition_expression | subtraction_expression | multiplication_expression | division_expression | string_concat_expression | single_expression
|
87
|
+
end
|
88
|
+
|
89
|
+
# Entry Point rule
|
90
|
+
root :expression_list
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module FormulaDSL
|
3
|
+
class Transformer
|
4
|
+
|
5
|
+
def apply( ast )
|
6
|
+
expression = parse(ast)
|
7
|
+
|
8
|
+
expression
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def parse( hash )
|
13
|
+
if hash.keys.size == 1
|
14
|
+
parse_single_key_hash( hash )
|
15
|
+
else
|
16
|
+
puts "como faz o parse de um hash com mais de 1 chave???"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_single_key_hash( hash )
|
21
|
+
key = hash.keys.first
|
22
|
+
if is_operator? key
|
23
|
+
parse_operation key, hash[key]
|
24
|
+
elsif is_function? key
|
25
|
+
parse_function hash[key]
|
26
|
+
else
|
27
|
+
puts "como faz o parse de um hash com chave = #{key}???"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_operator?( key )
|
32
|
+
[:+, :-, :*, :/].include?(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_function?(key)
|
36
|
+
:function == key
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_operation(operator, values={left: '', right: ''})
|
40
|
+
left_side = extract_operation_side(values[:left])
|
41
|
+
right_side = extract_operation_side(values[:right])
|
42
|
+
|
43
|
+
BinaryExpression.new(operator, left_side, right_side)
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_function( hash = {name:'', args:''} )
|
47
|
+
FunctionExpression.new(hash[:name].to_str, hash[:args].to_str)
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_operation_side( side )
|
51
|
+
if side.respond_to?(:keys)
|
52
|
+
parse( side )
|
53
|
+
else
|
54
|
+
if (/"(?<value>.)"/ =~ side.to_str)
|
55
|
+
"'#{value}'"
|
56
|
+
elsif (/(?<value>\d+)/ =~ side.str)
|
57
|
+
value.to_i
|
58
|
+
else
|
59
|
+
side.to_str
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/formula_dsl.rb
CHANGED
@@ -4,4 +4,11 @@ require "bundler/setup"
|
|
4
4
|
Bundler.require(:default)
|
5
5
|
|
6
6
|
require 'parslet'
|
7
|
-
require 'formula_dsl/
|
7
|
+
require 'formula_dsl/parser'
|
8
|
+
require 'formula_dsl/transformer'
|
9
|
+
require 'formula_dsl/functions'
|
10
|
+
require 'formula_dsl/binary_operations'
|
11
|
+
require 'formula_dsl/function_expression_factory'
|
12
|
+
require 'formula_dsl/binary_expression_factory'
|
13
|
+
require 'formula_dsl/binary_expression'
|
14
|
+
require 'formula_dsl/function_expression'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module FormulaDSL
|
5
|
+
describe BinaryExpressionFactory do
|
6
|
+
subject{ BinaryExpressionFactory }
|
7
|
+
|
8
|
+
specify { should respond_to(:new) }
|
9
|
+
|
10
|
+
it "should be return a ADDITON Proc to + operator " do
|
11
|
+
subject.new(:+).should be == FormulaDSL::BinaryOperations::ADDITION
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be return a SUBTRACTION Proc to - operator " do
|
15
|
+
subject.new(:-).should be == FormulaDSL::BinaryOperations::SUBTRACTION
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be return a MULTIPLICATON Proc to * operator " do
|
19
|
+
subject.new(:*).should be == FormulaDSL::BinaryOperations::MULTIPLICATION
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be return a DIVISION Proc to / operator " do
|
23
|
+
subject.new(:/).should be == FormulaDSL::BinaryOperations::DIVISION
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise FormulaDSL::MissingBinaryOperation for a unknown operator " do
|
27
|
+
lambda{ subject.new('abc') }.should raise_error(MissingBinaryOperationError)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module FormulaDSL
|
5
|
+
describe BinaryExpression do
|
6
|
+
subject{ BinaryExpression.new(:+, 3, 2) }
|
7
|
+
|
8
|
+
specify { should respond_to(:operator) }
|
9
|
+
specify { should respond_to(:left_term) }
|
10
|
+
specify { should respond_to(:right_term) }
|
11
|
+
specify { should respond_to(:apply) }
|
12
|
+
|
13
|
+
it "should be considered equal to a another BinaryExpression if that function contains same data" do
|
14
|
+
subject.should == BinaryExpression.new(:+, 3, 2)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module FormulaDSL
|
5
|
+
describe FunctionExpressionFactory do
|
6
|
+
subject{ FunctionExpressionFactory }
|
7
|
+
|
8
|
+
specify { should respond_to(:new) }
|
9
|
+
|
10
|
+
it "should return a correct function implementation for a given function name" do
|
11
|
+
constant = subject.new("month")
|
12
|
+
constant.should be FormulaDSL::Functions::MONTH
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise MissingFunctionError for a not implemented function" do
|
16
|
+
lambda{ subject.new("sqrt") }.should raise_error(MissingFunctionError)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
context "with private method #constantize" do
|
21
|
+
it "should receive a string with a full qualified function name and return the correspondent Constant" do
|
22
|
+
constant = subject.send(:constantize, "FormulaDSL::Functions::MONTH")
|
23
|
+
constant.should be FormulaDSL::Functions::MONTH
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module FormulaDSL
|
5
|
+
describe FunctionExpression do
|
6
|
+
subject{ FunctionExpression.new('SQRT', 4) }
|
7
|
+
|
8
|
+
specify { should respond_to(:name) }
|
9
|
+
specify { should respond_to(:args) }
|
10
|
+
specify { should respond_to(:apply) }
|
11
|
+
|
12
|
+
it "should be considered equal to a another Function if that function contains same data" do
|
13
|
+
subject.should == FunctionExpression.new('SQRT', 4)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module FormulaDSL
|
5
|
+
describe Parser do
|
6
|
+
|
7
|
+
let(:parser){ subject }
|
8
|
+
|
9
|
+
context "Single Expression's" do
|
10
|
+
|
11
|
+
it "should recognize expression '2 + 1' " do
|
12
|
+
ast = parser.parse('2 + 1')
|
13
|
+
ast.to_s.should == %Q({:+=>{:left=>"2"@0, :right=>"1"@4}})
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should recognize expression '2 + 1 + 3' " do
|
17
|
+
ast = parser.parse('2 + 1 + 3')
|
18
|
+
ast.to_s.should == %Q({:+=>{:left=>{:+=>{:left=>"2"@0, :right=>"1"@4}}, :right=>"3"@8}})
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should recognize expression '2 - 1" do
|
22
|
+
ast = parser.parse('2 - 1')
|
23
|
+
ast.to_s.should == %Q({:-=>{:left=>"2"@0, :right=>"1"@4}})
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should recognize expression '2 * 1" do
|
27
|
+
ast = parser.parse('2 * 1')
|
28
|
+
ast.to_s.should == %Q({:*=>{:left=>"2"@0, :right=>"1"@4}})
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should recognize expression '2 / 2" do
|
32
|
+
ast = parser.parse('2 / 2')
|
33
|
+
ast.to_s.should == %Q({:/=>{:left=>"2"@0, :right=>"2"@4}})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "Composed expression's " do
|
38
|
+
it "should recognize a function like 'Month(data)' " do
|
39
|
+
ast = parser.parse('Month(data)')
|
40
|
+
ast.to_s.should == %Q({:function=>{:name=>"Month"@0, :args=>"data"@6}})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should recognize the expression 'Month(data) - 1' " do
|
44
|
+
ast = parser.parse('Month(data) - 1')
|
45
|
+
ast.to_s.should == %Q({:-=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"1"@14}})
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should recognize the expression 'Month(data) * 1' " do
|
49
|
+
ast = parser.parse('Month(data) * 1')
|
50
|
+
ast.to_s.should == %Q({:*=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"1"@14}})
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should recognize the expression 'Month(data) / 1' " do
|
54
|
+
ast = parser.parse('Month(data) / 1')
|
55
|
+
ast.to_s.should == %Q({:/=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"1"@14}})
|
56
|
+
end
|
57
|
+
|
58
|
+
it " should recognize expression with 2 functions 'Month(data) + Year(data)'" do
|
59
|
+
ast = parser.parse('Month(data) + Year(data)')
|
60
|
+
ast.to_s.should == %Q({:+=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>{:function=>{:name=>"Year"@14, :args=>"data"@19}}}})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "Expressions with string concatenation" do
|
65
|
+
it " should recognize expression with string concat \"A\" + \"B\"'" do
|
66
|
+
ast = parser.parse('"A" + "B"')
|
67
|
+
ast.to_s.should == %Q({:+=>{:left=>"\\\"A\\\""@0, :right=>"\\\"B\\\""@6}})
|
68
|
+
end
|
69
|
+
|
70
|
+
it " should recognize expression with string concat '\"A\" + \"B\" + \"C\" ' " do
|
71
|
+
ast = parser.parse('"A" + "B" + "C"')
|
72
|
+
ast.to_s.should == %Q({:+=>{:left=>{:+=>{:left=>"\\\"A\\\""@0, :right=>"\\\"B\\\""@6}}, :right=>"\\\"C\\\""@12}})
|
73
|
+
end
|
74
|
+
|
75
|
+
it " should recognize expression with string concat 'Month(data) + \"/\"'" do
|
76
|
+
ast = parser.parse('Month(data) + "/"')
|
77
|
+
ast.to_s.should == %Q({:+=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"\\\"/\\\""@14}})
|
78
|
+
end
|
79
|
+
|
80
|
+
it " should recognize expression with string concat 'Month(data) + \"/\" + Year(data)'" do
|
81
|
+
ast = parser.parse('Month(data) + "/" + Year(data)')
|
82
|
+
ast.to_s.should == %Q({:+=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>{:+=>{:left=>"\\\"/\\\""@14, :right=>{:function=>{:name=>"Year"@20, :args=>"data"@25}}}}}})
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module FormulaDSL
|
5
|
+
describe Transformer do
|
6
|
+
|
7
|
+
let(:parser){ Parser.new }
|
8
|
+
let(:resolver){ subject }
|
9
|
+
|
10
|
+
context "Resolving Expression's" do
|
11
|
+
|
12
|
+
it "should resolve ast '2 + 1' as BinaryExpression" do
|
13
|
+
ast = parser.parse('2 + 1')
|
14
|
+
expression = resolver.apply( ast )
|
15
|
+
expression.should be_a_kind_of BinaryExpression
|
16
|
+
expression.operator.should == :+
|
17
|
+
expression.left_term.should == 2
|
18
|
+
expression.right_term.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should resolve ast '2 + 1 - 4' as BinaryExpression with contains antoher BinaryExpression" do
|
22
|
+
ast = parser.parse('2 + 1 - 4')
|
23
|
+
expression = resolver.apply( ast )
|
24
|
+
expression.should be_a_kind_of BinaryExpression
|
25
|
+
expression.left_term.should be_a_kind_of BinaryExpression
|
26
|
+
expression.left_term.should be == BinaryExpression.new(:+,2,1)
|
27
|
+
expression.right_term.should == 4
|
28
|
+
expression.operator.should == :-
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should resolve ast 'Month(data) as FunctionExpression" do
|
32
|
+
ast = parser.parse('Month(data)')
|
33
|
+
expression = resolver.apply( ast )
|
34
|
+
expression.should be_a_kind_of FunctionExpression
|
35
|
+
expression.name.should == 'Month'
|
36
|
+
expression.args.should == ['data']
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should resolve ast 'Month(data) + Year(data) as Binary operation wich conatins 2 FunctionExpression as left and right terms" do
|
40
|
+
ast = parser.parse('Month(data) + Year(data)')
|
41
|
+
expression = resolver.apply( ast )
|
42
|
+
expression.should be_a_kind_of BinaryExpression
|
43
|
+
expression.operator.should == :+
|
44
|
+
expression.left_term.should be == FunctionExpression.new('Month','data')
|
45
|
+
expression.right_term.should be == FunctionExpression.new('Year','data')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should resolve 'Month(data) + \"/\" + Year(data)' as BinaryExpression wich contains FunctionExpression and another BinaryExpression" do
|
49
|
+
ast = parser.parse('Month(data) + "/" + Year(data)')
|
50
|
+
expression = resolver.apply( ast )
|
51
|
+
expression.should be_a_kind_of BinaryExpression
|
52
|
+
expression.operator.should == :+
|
53
|
+
expression.left_term.should == FunctionExpression.new('Month','data')
|
54
|
+
expression.right_term.should == BinaryExpression.new(:+,"'/'",FunctionExpression.new('Year','data'))
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: formula_dsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03
|
12
|
+
date: 2012-04-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: parslet
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,40 +21,60 @@ dependencies:
|
|
21
21
|
version: 1.3.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.3.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.9.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.9.0
|
25
46
|
- !ruby/object:Gem::Dependency
|
26
47
|
name: jeweler
|
27
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
28
49
|
none: false
|
29
50
|
requirements:
|
30
51
|
- - ~>
|
31
52
|
- !ruby/object:Gem::Version
|
32
53
|
version: 1.8.3
|
33
|
-
type: :
|
54
|
+
type: :development
|
34
55
|
prerelease: false
|
35
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.8.3
|
36
62
|
- !ruby/object:Gem::Dependency
|
37
63
|
name: rake
|
38
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
39
65
|
none: false
|
40
66
|
requirements:
|
41
|
-
- -
|
67
|
+
- - ! '>'
|
42
68
|
- !ruby/object:Gem::Version
|
43
69
|
version: 0.9.2
|
44
|
-
type: :
|
70
|
+
type: :development
|
45
71
|
prerelease: false
|
46
|
-
version_requirements:
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: rspec
|
49
|
-
requirement: &70351729393780 !ruby/object:Gem::Requirement
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
73
|
none: false
|
51
74
|
requirements:
|
52
|
-
- -
|
75
|
+
- - ! '>'
|
53
76
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
type: :runtime
|
56
|
-
prerelease: false
|
57
|
-
version_requirements: *70351729393780
|
77
|
+
version: 0.9.2
|
58
78
|
description: ! ' External DSL to express math operations like you do in MSExcel formula
|
59
79
|
bar. The parser generate a AST (Hash) for a given expression. '
|
60
80
|
email: sergior.jr@gmail.com
|
@@ -75,8 +95,20 @@ files:
|
|
75
95
|
- VERSION
|
76
96
|
- formula_dsl.gemspec
|
77
97
|
- lib/formula_dsl.rb
|
78
|
-
- lib/formula_dsl/
|
79
|
-
-
|
98
|
+
- lib/formula_dsl/binary_expression.rb
|
99
|
+
- lib/formula_dsl/binary_expression_factory.rb
|
100
|
+
- lib/formula_dsl/binary_operations.rb
|
101
|
+
- lib/formula_dsl/function_expression.rb
|
102
|
+
- lib/formula_dsl/function_expression_factory.rb
|
103
|
+
- lib/formula_dsl/functions.rb
|
104
|
+
- lib/formula_dsl/parser.rb
|
105
|
+
- lib/formula_dsl/transformer.rb
|
106
|
+
- spec/formula_dsl/binary_expression_factory_spec.rb
|
107
|
+
- spec/formula_dsl/binary_expression_spec.rb
|
108
|
+
- spec/formula_dsl/function_expression_factory_spec.rb
|
109
|
+
- spec/formula_dsl/function_expression_spec.rb
|
110
|
+
- spec/formula_dsl/parser_spec.rb
|
111
|
+
- spec/formula_dsl/transformer_spec.rb
|
80
112
|
- spec/spec_helper.rb
|
81
113
|
homepage: http://github.com/sergiojunior/formula_dsl
|
82
114
|
licenses:
|
@@ -93,7 +125,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
125
|
version: '0'
|
94
126
|
segments:
|
95
127
|
- 0
|
96
|
-
hash:
|
128
|
+
hash: 2530953314598159526
|
97
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
130
|
none: false
|
99
131
|
requirements:
|
@@ -102,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
134
|
version: '0'
|
103
135
|
requirements: []
|
104
136
|
rubyforge_project:
|
105
|
-
rubygems_version: 1.8.
|
137
|
+
rubygems_version: 1.8.21
|
106
138
|
signing_key:
|
107
139
|
specification_version: 3
|
108
140
|
summary: External DSL that creates AST representation (Hash) for a given mathematical
|
@@ -1,90 +0,0 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
class FormulaParser < Parslet::Parser
|
3
|
-
|
4
|
-
# Single char rules
|
5
|
-
rule(:lparen) { str('(') >> space? }
|
6
|
-
rule(:rparen) { str(')') >> space? }
|
7
|
-
rule(:comma) { str(',') >> space? }
|
8
|
-
|
9
|
-
# non significant character's
|
10
|
-
rule(:space) { match('\s').repeat(1) }
|
11
|
-
rule(:space?) { space.maybe }
|
12
|
-
|
13
|
-
# Literal's
|
14
|
-
rule(:number) { match('\d').repeat(1) }
|
15
|
-
rule(:quote) { match('"') }
|
16
|
-
rule(:string) { quote >> space? >> match('.') >> space? >> quote}
|
17
|
-
rule(:identifier) { match['\w'].repeat(1) }
|
18
|
-
|
19
|
-
# Argument
|
20
|
-
rule (:argument) { identifier | number }
|
21
|
-
rule (:arglist) { (argument >> comma.maybe).repeat }
|
22
|
-
|
23
|
-
# Operators
|
24
|
-
rule(:add_operator ) { match(['+']) }
|
25
|
-
rule(:sub_operator ) { match('-') }
|
26
|
-
rule(:mult_operator) { match(['*']) }
|
27
|
-
rule(:div_operator ) { match(['/']) }
|
28
|
-
|
29
|
-
rule :operator do
|
30
|
-
add_operator | sub_operator | mult_operator | div_operator
|
31
|
-
end
|
32
|
-
|
33
|
-
#binary operations + - / *
|
34
|
-
rule :addition do
|
35
|
-
( number.as(:left) >> space? >> add_operator >> space? >> ( number ).as(:right) ).as(:+)
|
36
|
-
end
|
37
|
-
|
38
|
-
rule :subtraction do
|
39
|
-
( number.as(:left) >> space? >> sub_operator >> space? >> ( number ).as(:right) ).as(:-)
|
40
|
-
end
|
41
|
-
|
42
|
-
rule :multiplication do
|
43
|
-
( number.as(:left) >> space? >> mult_operator >> space? >> ( number ).as(:right) ).as(:*)
|
44
|
-
end
|
45
|
-
|
46
|
-
rule :division do
|
47
|
-
( number.as(:left) >> space? >> div_operator >> space? >> ( number ).as(:right) ).as(:/)
|
48
|
-
end
|
49
|
-
|
50
|
-
rule :function do
|
51
|
-
(identifier.as(:name) >> lparen >> arglist.as(:args) >> rparen).as(:function)
|
52
|
-
end
|
53
|
-
|
54
|
-
rule :string_concat do
|
55
|
-
( string.as(:left) >> space? >> add_operator >> space? >> ( string ).as(:right) ).as(:concat)
|
56
|
-
end
|
57
|
-
|
58
|
-
rule :single_expression do
|
59
|
-
function | addition | subtraction | multiplication | division | string_concat | number | string
|
60
|
-
end
|
61
|
-
|
62
|
-
# expression with other expression (compose expression)
|
63
|
-
# Ex: expression >> some_op >> number === [ Mont(data) - 1 ]
|
64
|
-
rule :subtraction_expression do
|
65
|
-
(single_expression.as(:left) >> space? >> sub_operator >> space? >> (single_expression | number).as(:right) ).as(:-)
|
66
|
-
end
|
67
|
-
|
68
|
-
rule :addition_expression do
|
69
|
-
(single_expression.as(:left) >> space? >> add_operator >> space? >> (single_expression | number).as(:right) ).as(:+)
|
70
|
-
end
|
71
|
-
|
72
|
-
rule :multiplication_expression do
|
73
|
-
(single_expression.as(:left) >> space? >> mult_operator >> space? >> (single_expression | number).as(:right) ).as(:*)
|
74
|
-
end
|
75
|
-
|
76
|
-
rule :division_expression do
|
77
|
-
(single_expression.as(:left) >> space? >> div_operator >> space? >> (single_expression | number).as(:right) ).as(:/)
|
78
|
-
end
|
79
|
-
|
80
|
-
rule :string_concat_expression do
|
81
|
-
(single_expression.as(:left) >> space? >> add_operator >> space? >> ( expression_list | string ).as(:right) ).as(:concat)
|
82
|
-
end
|
83
|
-
|
84
|
-
rule :expression_list do
|
85
|
-
string_concat_expression | addition_expression | subtraction_expression | multiplication_expression | division_expression | single_expression
|
86
|
-
end
|
87
|
-
|
88
|
-
# Entry Point rule
|
89
|
-
root :expression_list
|
90
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe FormulaParser do
|
5
|
-
|
6
|
-
let(:parser){ subject }
|
7
|
-
|
8
|
-
context "Single Expression's" do
|
9
|
-
|
10
|
-
it "should recognize expression '2 + 1' " do
|
11
|
-
ast = parser.parse('2 + 1')
|
12
|
-
ast.to_s.should == %Q({:+=>{:left=>"2"@0, :right=>"1"@4}})
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should recognize expression '2 - 1" do
|
16
|
-
ast = parser.parse('2 - 1')
|
17
|
-
ast.to_s.should == %Q({:-=>{:left=>"2"@0, :right=>"1"@4}})
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should recognize expression '2 * 1" do
|
21
|
-
ast = parser.parse('2 * 1')
|
22
|
-
ast.to_s.should == %Q({:*=>{:left=>"2"@0, :right=>"1"@4}})
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should recognize expression '2 / 2" do
|
26
|
-
ast = parser.parse('2 / 2')
|
27
|
-
ast.to_s.should == %Q({:/=>{:left=>"2"@0, :right=>"2"@4}})
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context "Composed expression's " do
|
32
|
-
it "should recognize a function like 'Month(data)' " do
|
33
|
-
ast = parser.parse('Month(data)')
|
34
|
-
ast.to_s.should == %Q({:function=>{:name=>"Month"@0, :args=>"data"@6}})
|
35
|
-
end
|
36
|
-
|
37
|
-
it "should recognize the expression 'Month(data) - 1' " do
|
38
|
-
ast = parser.parse('Month(data) - 1')
|
39
|
-
ast.to_s.should == %Q({:-=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"1"@14}})
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should recognize the expression 'Month(data) * 1' " do
|
43
|
-
ast = parser.parse('Month(data) * 1')
|
44
|
-
ast.to_s.should == %Q({:*=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"1"@14}})
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should recognize the expression 'Month(data) / 1' " do
|
48
|
-
ast = parser.parse('Month(data) / 1')
|
49
|
-
ast.to_s.should == %Q({:/=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"1"@14}})
|
50
|
-
end
|
51
|
-
|
52
|
-
it " should recognize expression with 2 functions 'Month(data) + Year(data)'" do
|
53
|
-
ast = parser.parse('Month(data) + Year(data)')
|
54
|
-
ast.to_s.should == %Q({:concat=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>{:function=>{:name=>"Year"@14, :args=>"data"@19}}}})
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context "Expressions with string concatenation" do
|
59
|
-
it " should recognize expression with string concat \"A\" + \"B\"'" do
|
60
|
-
ast = parser.parse('"A" + "B"')
|
61
|
-
ast.to_s.should == %Q({:concat=>{:left=>"\\\"A\\\""@0, :right=>"\\\"B\\\""@6}})
|
62
|
-
end
|
63
|
-
|
64
|
-
it " should recognize expression with string concat '\"A\" + \"B\" + \"C\" ' " do
|
65
|
-
ast = parser.parse('"A" + "B" + "C"')
|
66
|
-
ast.to_s.should == %Q({:concat=>{:left=>{:concat=>{:left=>"\\\"A\\\""@0, :right=>"\\\"B\\\""@6}}, :right=>"\\\"C\\\""@12}})
|
67
|
-
end
|
68
|
-
|
69
|
-
it " should recognize expression with string concat 'Month(data) + \"/\"'" do
|
70
|
-
ast = parser.parse('Month(data) + "/"')
|
71
|
-
ast.to_s.should == %Q({:concat=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>"\\\"/\\\""@14}})
|
72
|
-
end
|
73
|
-
|
74
|
-
it " should recognize expression with string concat 'Month(data) + \"/\" + Year(data)'" do
|
75
|
-
ast = parser.parse('Month(data) + "/" + Year(data)')
|
76
|
-
ast.to_s.should == %Q({:concat=>{:left=>{:function=>{:name=>"Month"@0, :args=>"data"@6}}, :right=>{:concat=>{:left=>"\\\"/\\\""@14, :right=>{:function=>{:name=>"Year"@20, :args=>"data"@25}}}}}})
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|