format_engine 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d7dfb82f1660f588d8c962202bc052410d0103a
4
- data.tar.gz: afc3a10a88f09024a9d37493bf77308be189473b
3
+ metadata.gz: dbd9eddd61c278dea142573e9edc3ff65aa622f9
4
+ data.tar.gz: 5d4637bb827e30c3230cb44b9f4e02d8640af24e
5
5
  SHA512:
6
- metadata.gz: 5f0d437c4b5d083b40287823fe705cb3a09871b68b8ed8805eb09e64f5d28dbb80779fb5f471f261d1ff9e84d0ba4fcd01c8f0ee78fd32b7bc0fcaa4201a585d
7
- data.tar.gz: 110691ceb8a920df1b626febe8f803402e2b8ad48fe7934bf5cd06a6f0d84e8f04bcacd404c2c885a1fa9da7ccfc4fbf432585ce5089345bf5bac18e432f60a1
6
+ metadata.gz: ec88dfaf16365f492b9ab47f15885a476218e7e610a74726b9199d077a5ae061579304d5f6d7aceb8612d39abb8757c3003731d8f223d04fc07aa188158fbe0d
7
+ data.tar.gz: 8c4926a9a3abba6ac9fc2b36155e08a8f987714201da795891106aab22ade47887b4ed2aa9a731d21158591649272513e742e26739de4b0a22af27bd74dd4d82
data/README.md CHANGED
@@ -88,12 +88,15 @@ puts cust.strfmt('%f %l is %a years old.')
88
88
 
89
89
  Format String Specification Syntax (BNF):
90
90
 
91
- * spec = ( text | item )+
92
- * item = "%" flag* ("+" | "-")?(parm ("." parm)?)? command
93
- * flag = "~"|"@"|"#"|"&"|"^"|"&"|"*"|"="|"?"|"_"|"<"|">"|"\\"|"/"|"."|","|"|"|"!"
94
- * parm = ("0" .. "9")+
95
- * command = ("a" .. "z" | "A" .. "Z")
96
-
91
+ * spec ::= (text | item | set)+
92
+ * item ::= "%" flag* sign? (parm ("." parm)? )? command
93
+ * set ::= "%" flag* parm? "[" chrs "]"
94
+ * flag ::= "~" | "@" | "#" | "&" | "^" | "&" | "*" | "=" | "?" | "_"
95
+ | "<" | ">" | "\\" | "/" | "." | "," | "|" | "!"
96
+ * sign ::= sign = ("+" | "-")
97
+ * parm ::= ("0" .. "9")+
98
+ * chrs ::= (not_any("]") | "\\" "]")+
99
+ * command ::= ("a" .. "z" | "A" .. "Z")
97
100
 
98
101
  ###Samples:
99
102
 
data/lib/format_engine.rb CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  require 'English'
4
4
 
5
- require 'format_engine/format_spec'
6
- require 'format_engine/engine'
7
- require 'format_engine/spec_info'
8
- require 'format_engine/attr_formatter'
9
- require 'format_engine/attr_parser'
5
+ require_relative 'format_engine/format_spec'
6
+ require_relative 'format_engine/engine'
7
+ require_relative 'format_engine/spec_info'
8
+ require_relative 'format_engine/attr_formatter'
9
+ require_relative 'format_engine/attr_parser'
10
10
 
11
- require 'format_engine/version'
11
+ require_relative 'format_engine/version'
@@ -3,24 +3,27 @@ module FormatEngine
3
3
  # The engine class of the format engine.
4
4
  class Engine
5
5
 
6
+ #The parse library
7
+ attr_reader :library
8
+
6
9
  #Set up base data structures.
7
10
  def initialize(library)
8
- @lib = library
11
+ @library = library
9
12
 
10
13
  #Set up defaults for pre and post amble blocks.
11
14
  nop = lambda { }
12
- @lib[:before] ||= nop
13
- @lib[:after] ||= nop
15
+ @library[:before] ||= nop
16
+ @library[:after] ||= nop
14
17
  end
15
18
 
16
19
  # Get an entry from the library
17
20
  def [](index)
18
- @lib[index]
21
+ @library[index]
19
22
  end
20
23
 
21
24
  # Set an entry in the library
22
25
  def []=(index, value)
23
- @lib[index] = value
26
+ @library[index] = value
24
27
  end
25
28
 
26
29
  #Do the actual work of building the formatted output.
@@ -1,14 +1,20 @@
1
+ #Analysis of format/parse specification strings.
2
+
1
3
  require_relative 'format_spec/literal'
2
4
  require_relative 'format_spec/variable'
5
+ require_relative 'format_spec/set'
3
6
 
4
7
  # Format String Specification Syntax (BNF):
5
- # spec = (text | item)+
6
- # item = "%" flag* (parm ("." parm)? )? command
8
+ # spec = (text | item | set)+
9
+ # item = "%" flag* sign? (parm ("." parm)? )? command
10
+ # set = "%" flag* parm? "[" chrs "]"
7
11
  # flag = ( "~" | "@" | "#" | "&" | "^" |
8
12
  # "&" | "*" | "-" | "+" | "=" |
9
13
  # "?" | "_" | "<" | ">" | "\\" |
10
14
  # "/" | "." | "," | "|" | "!" )
15
+ # sign = ("+" | "-")
11
16
  # parm = ("0" .. "9" )+
17
+ # chrs = (not_any("]") | "\\" "]")+
12
18
  # command = ("a" .. "z" | "A" .. "Z")
13
19
  #
14
20
  # Sample: x = FormatSpec.get_spec "Elapsed = %*3.1H:%02M!"
@@ -18,10 +24,12 @@ module FormatEngine
18
24
  #The format string parser.
19
25
  class FormatSpec
20
26
  #The regex used to parse variable specifications.
21
- VAR_REGEX = %r{(?<flags> [~@#$^&*\=?_<>\\\/\.,\|!]*){0}
22
- (?<parms> [-+]?(\d+(\.\d+)?)?){0}
23
- (%\g<flags>\g<parms>[a-zA-Z])
24
- }x
27
+ REGEX = %r{(?<flags> [~@#$^&*\=?_<>\\\/\.,\|!]*){0}
28
+ (?<parms> [-+]?(\d+(\.\d+)?)?){0}
29
+ (?<var> %\g<flags>\g<parms>[a-zA-Z]){0}
30
+ (?<set> %\g<flags>\d*\[([^\]]|\\\])+\]){0}
31
+ \g<var> | \g<set>
32
+ }x
25
33
 
26
34
  #Don't use new, use get_spec instead.
27
35
  private_class_method :new
@@ -47,10 +55,17 @@ module FormatEngine
47
55
  #Scan the format string extracting literals and variables.
48
56
  def scan_spec(fmt_string)
49
57
  until fmt_string.empty?
50
- if fmt_string =~ VAR_REGEX
51
- @specs << FormatLiteral.new($PREMATCH) unless $PREMATCH.empty?
52
- @specs << FormatVariable.new($MATCH)
53
- fmt_string = $POSTMATCH
58
+ if (match_data = REGEX.match(fmt_string))
59
+ mid = match_data.to_s
60
+ pre = match_data.pre_match
61
+
62
+ @specs << FormatLiteral.new(pre) unless pre.empty?
63
+ @specs << case
64
+ when match_data[:var] then FormatVariable.new(mid)
65
+ when match_data[:set] then FormatSet.new(mid)
66
+ else fail "Impossible case in scan_spec."
67
+ end
68
+ fmt_string = match_data.post_match
54
69
  else
55
70
  @specs << FormatLiteral.new(fmt_string)
56
71
  fmt_string = ""
@@ -0,0 +1,55 @@
1
+ module FormatEngine
2
+
3
+ #A format engine set specification.
4
+ class FormatSet
5
+
6
+ #The fixed part of this set specification.
7
+ attr_reader :format
8
+
9
+ #The regular expression part of this set specification.
10
+ attr_reader :regex
11
+
12
+ #Setup a variable format specification.
13
+ def initialize(format)
14
+ @raw = format
15
+
16
+ if format =~ /(\d+)(?=\[)/
17
+ qualifier = "{1,#{$MATCH}}"
18
+ @format = $PREMATCH + "["
19
+ set = $POSTMATCH
20
+ elsif format =~ /\[/
21
+ qualifier = "+"
22
+ @format = $PREMATCH + $MATCH
23
+ set = $MATCH + $POSTMATCH
24
+ else
25
+ fail "Invalid set string #{format}"
26
+ end
27
+
28
+ @regex = Regexp.new("#{set}#{qualifier}")
29
+ end
30
+
31
+ #Is this variable supported by the engine?
32
+ def validate(engine)
33
+ fail "Unsupported tag = #{format.inspect}" unless engine[format]
34
+ self
35
+ end
36
+
37
+ #Format onto the output string
38
+ def do_format(spec_info)
39
+ fail "The tag %{@raw} may not be used in formatting."
40
+ end
41
+
42
+ #Parse from the input string
43
+ def do_parse(spec_info)
44
+ spec_info.fmt = self
45
+ spec_info.instance_exec(&spec_info.engine[self.format])
46
+ end
47
+
48
+ #Inspect for debugging.
49
+ def inspect
50
+ "Set(#{format.inspect}, #{regex.inspect})"
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -67,6 +67,12 @@ module FormatEngine
67
67
  has_prec? ? parms[1] : ""
68
68
  end
69
69
 
70
+ #Build up a regular expression for parsing.
71
+ def regex(base)
72
+ qualifier = has_width? ? "{1,#{width}}": "+"
73
+ Regexp.new("#{base}#{qualifier}")
74
+ end
75
+
70
76
  #Format onto the output string
71
77
  def do_format(spec_info)
72
78
  spec_info.fmt = self
@@ -1,5 +1,5 @@
1
1
 
2
2
  module FormatEngine
3
3
  # The version of the format_engine gem.
4
- VERSION = "0.3.2"
4
+ VERSION = "0.4.0"
5
5
  end
data/sire.rb ADDED
@@ -0,0 +1,85 @@
1
+ # coding: utf-8
2
+ # A Simple Interactive Ruby Environment
3
+
4
+ require_relative 'lib/format_engine'
5
+
6
+ $no_alias_read_line_module = true
7
+ require 'mini_readline'
8
+ require 'pp'
9
+
10
+ class Object
11
+ #Generate the class lineage of the object.
12
+ def classes
13
+ begin
14
+ result = ""
15
+ klass = self.instance_of?(Class) ? self : self.class
16
+
17
+ begin
18
+ result << klass.to_s
19
+ klass = klass.superclass
20
+ result << " < " if klass
21
+ end while klass
22
+
23
+ result
24
+ end
25
+ end
26
+ end
27
+
28
+ class SIRE
29
+ #Set up the interactive session.
30
+ def initialize
31
+ @_done = false
32
+
33
+ puts "Welcome to a Simple Interactive Ruby Environment\n"
34
+ puts "FormatEngine version = #{FormatEngine::VERSION}"
35
+ puts "Use command 'q' to quit.\n\n"
36
+ end
37
+
38
+ #Quit the interactive session.
39
+ def q
40
+ @_done = true
41
+ puts
42
+ "Bye bye for now!"
43
+ end
44
+
45
+ def fs
46
+ FormatEngine::FormatSpec
47
+ end
48
+
49
+ #Execute a single line.
50
+ def exec_line(line)
51
+ result = eval line
52
+ pp result unless line.length == 0
53
+
54
+ rescue Interrupt => e
55
+ @_break = true
56
+ puts "\nExecution Interrupted!"
57
+ puts "\n#{e.class} detected: #{e}\n"
58
+ puts e.backtrace
59
+ puts "\n"
60
+
61
+ rescue Exception => e
62
+ @_break = true
63
+ puts "\n#{e.class} detected: #{e}\n"
64
+ puts e.backtrace
65
+ puts
66
+ end
67
+
68
+ #Run the interactive session.
69
+ def run_sire
70
+ until @_done
71
+ @_break = false
72
+ exec_line(MiniReadline.readline('SIRE>', true))
73
+ end
74
+
75
+ puts "\n\n"
76
+
77
+ rescue Interrupt => e
78
+ puts "\nInterrupted! Program Terminating."
79
+ end
80
+
81
+ end
82
+
83
+ if __FILE__ == $0
84
+ SIRE.new.run_sire
85
+ end
@@ -25,6 +25,43 @@ class FormatSpecTester < Minitest::Test
25
25
  assert_equal(nil, test.specs[0].parms)
26
26
  end
27
27
 
28
+ def test_that_it_scans_set_formats
29
+ test = FormatEngine::FormatSpec.get_spec "%[A]"
30
+ assert_equal(Array, test.specs.class)
31
+ assert_equal(1, test.specs.length)
32
+ assert_equal(FormatEngine::FormatSet, test.specs[0].class)
33
+ assert_equal("%[", test.specs[0].format)
34
+ assert_equal(/[A]+/, test.specs[0].regex)
35
+
36
+ test = FormatEngine::FormatSpec.get_spec "%*[A]"
37
+ assert_equal(Array, test.specs.class)
38
+ assert_equal(1, test.specs.length)
39
+ assert_equal(FormatEngine::FormatSet, test.specs[0].class)
40
+ assert_equal("%*[", test.specs[0].format)
41
+ assert_equal(/[A]+/, test.specs[0].regex)
42
+
43
+ test = FormatEngine::FormatSpec.get_spec "%7[A]"
44
+ assert_equal(Array, test.specs.class)
45
+ assert_equal(1, test.specs.length)
46
+ assert_equal(FormatEngine::FormatSet, test.specs[0].class)
47
+ assert_equal("%[", test.specs[0].format)
48
+ assert_equal(/[A]{1,7}/, test.specs[0].regex)
49
+
50
+ test = FormatEngine::FormatSpec.get_spec "%*7[A]"
51
+ assert_equal(Array, test.specs.class)
52
+ assert_equal(1, test.specs.length)
53
+ assert_equal(FormatEngine::FormatSet, test.specs[0].class)
54
+ assert_equal("%*[", test.specs[0].format)
55
+ assert_equal(/[A]{1,7}/, test.specs[0].regex)
56
+ end
57
+
58
+ def test_a_mixed_set
59
+ test = FormatEngine::FormatSpec.get_spec "%f %l %[age] %a"
60
+ assert_equal(Array, test.specs.class)
61
+ assert_equal(7, test.specs.length)
62
+
63
+ end
64
+
28
65
  def test_that_it_scans_tab_seperators
29
66
  test = FormatEngine::FormatSpec.get_spec "%A\t%B"
30
67
  assert_equal(Array, test.specs.class)
@@ -32,6 +32,10 @@ class FormatterTester < Minitest::Test
32
32
  [make_formatter, make_person, make_spec(str)]
33
33
  end
34
34
 
35
+ def test_crazy
36
+ assert_equal("0.4.0", FormatEngine::VERSION)
37
+ end
38
+
35
39
  def test_that_it_can_format_normally
36
40
  engine, obj, spec = make_all("Name = %f %l %a")
37
41
  assert_equal("Name = Squidly Jones 21", engine.do_format(obj, spec))
@@ -20,6 +20,7 @@ class ParserTester < Minitest::Test
20
20
  "%l" => lambda { tmp[:ln] = found if parse(/(\w)+/ ) },
21
21
  "%L" => lambda { tmp[:ln] = found.upcase if parse(/(\w)+/) },
22
22
  "%-L" => lambda { tmp[:ln] = found.capitalize if parse(/(\w)+/) },
23
+ "%[" => lambda { parse! fmt.regex },
23
24
  "%t" => lambda { parse("\t") },
24
25
  "%!t" => lambda { parse!("\t") },
25
26
 
@@ -92,6 +93,18 @@ class ParserTester < Minitest::Test
92
93
  assert_equal(TestPerson, result.class)
93
94
  assert_equal("Squidly", result.first_name)
94
95
  assert_equal("Jones", result.last_name)
96
+ assert_equal(55, result.age)
97
+ end
98
+
99
+ def test_that_it_can_parse_sets
100
+ engine = make_parser
101
+ spec = "%f %l %[age] %a"
102
+ result = engine.do_parse("Squidly Jones age 55", TestPerson, spec)
103
+
104
+ assert_equal(TestPerson, result.class)
105
+ assert_equal("Squidly", result.first_name)
106
+ assert_equal("Jones", result.last_name)
107
+ assert_equal(55, result.age)
95
108
  end
96
109
 
97
110
  def test_that_it_can_detect_errors
@@ -0,0 +1,95 @@
1
+ require_relative '../lib/format_engine'
2
+ gem 'minitest'
3
+ require 'minitest/autorun'
4
+ require 'minitest_visible'
5
+
6
+
7
+ # Test the internals of the parser engine. This is not the normal interface.
8
+ class ScanTester < Minitest::Test
9
+
10
+ #Track mini-test progress.
11
+ MinitestVisible.track self, __FILE__
12
+
13
+ def make_parser
14
+ FormatEngine::Engine.new(
15
+ "%d" => lambda do
16
+ dst << found.to_i if parse(fmt.regex("\\d"))
17
+ end,
18
+
19
+ "%*d" => lambda do
20
+ parse(fmt.regex("\\d"))
21
+ end,
22
+
23
+ "%[" => lambda do
24
+ dst << found if parse(fmt.regex)
25
+ end,
26
+
27
+ "%*[" => lambda do
28
+ parse(fmt.regex)
29
+ end)
30
+ end
31
+
32
+ def test_that_it_can_scan
33
+ engine = make_parser
34
+ spec = "%d %2d %4d"
35
+ result = engine.do_parse("12 34 56", [], spec)
36
+
37
+ assert_equal(Array, result.class)
38
+ assert_equal([12, 34, 56] , result)
39
+ end
40
+
41
+ def test_missing_data
42
+ engine = make_parser
43
+ spec = "%d %2d %4d %d"
44
+ result = engine.do_parse("12 34 56", [], spec)
45
+
46
+ assert_equal(Array, result.class)
47
+ assert_equal([12, 34, 56] , result)
48
+ end
49
+
50
+ def test_excess_data
51
+ engine = make_parser
52
+ spec = "%d %2d %4d"
53
+ result = engine.do_parse("12 34 56 78", [], spec)
54
+
55
+ assert_equal(Array, result.class)
56
+ assert_equal([12, 34, 56] , result)
57
+ end
58
+
59
+ def test_skipped_data
60
+ engine = make_parser
61
+ spec = "%d %2d %*d %d"
62
+ result = engine.do_parse("12 34 56 78", [], spec)
63
+
64
+ assert_equal(Array, result.class)
65
+ assert_equal([12, 34, 78] , result)
66
+ end
67
+
68
+ def test_packed_data
69
+ engine = make_parser
70
+ spec = "%2d %2d %2d"
71
+ result = engine.do_parse("123456", [], spec)
72
+
73
+ assert_equal(Array, result.class)
74
+ assert_equal([12, 34, 56] , result)
75
+ end
76
+
77
+ def test_some_sets
78
+ engine = make_parser
79
+ spec = "%5[truefals] %5[truefals] %5[truefals]"
80
+ result = engine.do_parse("true false true", [], spec)
81
+
82
+ assert_equal(Array, result.class)
83
+ assert_equal(["true", "false", "true"] , result)
84
+ end
85
+
86
+ def test_some_sets_with_skips
87
+ engine = make_parser
88
+ spec = "%5[truefals] %*5[truefals] %5[truefals]"
89
+ result = engine.do_parse("true false true", [], spec)
90
+
91
+ assert_equal(Array, result.class)
92
+ assert_equal(["true", "true"] , result)
93
+ end
94
+
95
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: format_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Camilleri
@@ -101,6 +101,7 @@ files:
101
101
  - lib/format_engine/engine.rb
102
102
  - lib/format_engine/format_spec.rb
103
103
  - lib/format_engine/format_spec/literal.rb
104
+ - lib/format_engine/format_spec/set.rb
104
105
  - lib/format_engine/format_spec/variable.rb
105
106
  - lib/format_engine/spec_info.rb
106
107
  - lib/format_engine/version.rb
@@ -110,12 +111,14 @@ files:
110
111
  - mocks/demo/demo_parse.rb
111
112
  - mocks/test_person_mock.rb
112
113
  - reek.txt
114
+ - sire.rb
113
115
  - tests/engine_base_tests.rb
114
116
  - tests/format_engine_tests.rb
115
117
  - tests/format_spec_tests.rb
116
118
  - tests/formatter_engine_tests.rb
117
119
  - tests/literal_spec_tests.rb
118
120
  - tests/parser_engine_tests.rb
121
+ - tests/scan_tests.rb
119
122
  - tests/variable_spec_tests.rb
120
123
  homepage: http://teuthida-technologies.com/
121
124
  licenses: