mkbison 0.0.4 → 0.0.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95b6da8ba8118a59aede83a038e4f944bc775af0
4
- data.tar.gz: 324be9460b8a0a91c9ca3e5762169bb590dbe66c
3
+ metadata.gz: 4953dfa30988cbe416adf37cf53b7cc7cb6650e5
4
+ data.tar.gz: 4d2ca4c185047779a8f1aa998ee27b512ae161f7
5
5
  SHA512:
6
- metadata.gz: cafb5be27276443cdcda7a92314391c8a4c44e57327c5fe9169ec1985724743ac473eac8deb610fe1570a4977289da8df6047ac6fdc5db4b7d805332eb03f7cd
7
- data.tar.gz: 65ecb89dde9730af99122599bc73aef47198274e823ba1708259c01fa0ea26e1b7ed73fc872823b3f581a4cb787afaf3b041e863a5b1398ce3e0c65452a880ba
6
+ metadata.gz: 3b34b3be1c7e9212793c7b7a4d3efb4bed4ac290e575a54b95f8d0c3c258ed4b67d6e1c7422f7ebd86026ebeaec2c4a634a1813a29805c33e6d9dc6861cd96b6
7
+ data.tar.gz: 634ec92aa22ec51f382d316f32ea70c152f9a835ba40ccc802a2bb7166eef13b9a0cb3dc0d64fd5e031cd92c5b0031d58d893890a04d21252271a50be0e50e0d
data/.gitignore CHANGED
@@ -20,3 +20,6 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+
24
+ examples/*/ext
25
+ examples/*/lib
data/README.md CHANGED
@@ -1,43 +1,31 @@
1
1
  # mkbison
2
2
 
3
- TODO: Write a gem description
3
+ `mkbison` is a tool to create native ruby extensions containing [GNU Bison](https://www.gnu.org/software/bison/) generated LALR(1) grammar parsers.
4
4
 
5
5
  ## Installation
6
6
 
7
- Add this line to your application's Gemfile:
7
+ Add these lines to your application's Gemfile:
8
8
 
9
9
  gem 'mkbison'
10
+ gem 'rake-compiler'
10
11
 
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
- $ gem install mkbison
18
-
19
- You'll need to install [GNU Bison](https://www.gnu.org/software/bison/) as well, e.g.:
12
+ You'll need to install a GNU Bison version >= 3.0.0 as well, with e.g.:
20
13
 
21
14
  $ sudo apt-get install bison
22
15
 
23
16
  ## Usage and Example
24
17
 
25
- The gem installs a command `mkbison` which translates `.rby` files into `.y` Bison grammar files, and generates a native ruby extension to expose the resulting parser.
26
-
27
- `.rby` files contain a grammar definition and ruby code. Their syntax mostly mirrors that of Bison grammar files, with the following differences:
18
+ `mkbison` operates on grammar files with a syntax which mostly mirrors the Bison grammar. For example, here is a file which describes basic arithmetic:
28
19
 
29
- * Actions are written in ruby, rather than C (as is the lex section).
30
- * Positional references to semantic values are not allowed.
31
- * [Named references](http://www.gnu.org/software/bison/manual/html_node/Named-References.html) should not use a dollar sign `$`.
20
+ **[arithmetic.rby](https://github.com/wioux/mkbison/blob/master/examples/arithmetic/arithmetic.rby)**
21
+ ```yacc
32
22
 
33
- For example, here is a mkbison grammar for parsing very simple arithmetic expressions:
34
-
35
- **arithmetic.rby**
36
- ```
37
23
  %token NUMBER
24
+ %token LPAREN
25
+ %token RPAREN
38
26
 
39
- %left OP_PLUS
40
- %left OP_MINUS
27
+ %left OP_PLUS OP_MINUS
28
+ %left OP_TIMES OP_OVER
41
29
 
42
30
  %%
43
31
 
@@ -48,8 +36,14 @@ arithmetic:
48
36
 
49
37
  expression :
50
38
  NUMBER
39
+ | OP_MINUS expression[x]
40
+ { -x }
41
+ | LPAREN expression[x] RPAREN
42
+ { x }
51
43
  | addition
52
44
  | subtraction
45
+ | multiplication
46
+ | division
53
47
  ;
54
48
 
55
49
  addition :
@@ -62,30 +56,24 @@ subtraction :
62
56
  { left - right }
63
57
  ;
64
58
 
59
+ multiplication:
60
+ expression[left] OP_TIMES expression[right]
61
+ { left * right }
62
+ ;
63
+
64
+ division:
65
+ expression[left] OP_OVER expression[right]
66
+ { left / right }
67
+ ;
68
+
65
69
  %%
66
70
 
67
71
  class Arithmetic
68
72
  def lex
69
- # skip space
70
- while true
71
- while (c = self.read) && c =~ /\s/
72
- end
73
-
74
- if c == '#'
75
- while (char = self.read) && char != "\n"
76
- end
77
- else
78
- break
79
- end
80
- end
81
-
73
+ c = read_over_whitespace
82
74
  case c
83
75
  when '0'..'9'
84
- number = c
85
- while (c = self.peak) && ('0'..'9').include?(c)
86
- number << self.read
87
- end
88
-
76
+ number = read_integer(c)
89
77
  self.lex_value = number.to_i
90
78
  return Tokens::NUMBER
91
79
 
@@ -94,30 +82,65 @@ class Arithmetic
94
82
 
95
83
  when '-'
96
84
  return Tokens::OP_MINUS
97
- end
98
85
 
99
- nil
86
+ when '*'
87
+ return Tokens::OP_TIMES
88
+
89
+ when '/'
90
+ return Tokens::OP_OVER
91
+
92
+ when '('
93
+ return Tokens::LPAREN
94
+
95
+ when ')'
96
+ return Tokens::RPAREN
97
+
98
+ else
99
+ return nil
100
+ end
100
101
  end
101
102
  end
102
103
  ```
103
104
 
104
- To translate it into a Bison grammar file, run
105
+ The structure here should be recognizable to those familiar with GNU Bison. If you aren't, you might want to [read up on its usage](https://www.gnu.org/software/bison/manual/html_node/index.html) before using `mkbison`.
105
106
 
106
- $ bundle exec mkbison -n Arithmetic -o . arithmetic.rby
107
- $ rake compile
107
+ There are three sections:
108
+
109
+ 1) Token and precedence definitions
110
+ 2) The description of the grammar
111
+ 3) The lexing implementation
112
+
113
+ To compile this into a ruby extension which we can use to calculate basic arithmetic expressions, first run `mkbison`:
114
+
115
+ $ bundle exec mkbison -n Arithmetic arithmetic.rby
116
+
117
+ This creates the ruby extension in the current directory (under `lib/` and `ext/` directories). The Bison translation of the grammar can be found at `ext/arithmetic/arithmetic.y`. Once compiled, we'll be able to require it as `'arithmetic'` and use it with `Arithmetic.new(expression).parse`.
118
+
119
+ Next we'll need to create a `rake` task as follows:
120
+
121
+ **[Rakefile](https://github.com/wioux/mkbison/blob/master/examples/arithmetic/Rakefile)**
122
+ ```ruby
123
+ require "rake/extensiontask"
124
+
125
+ Rake::ExtensionTask.new "arithmetic" do |ext|
126
+ ext.lib_dir = "lib/arithmetic"
127
+ end
128
+ ```
108
129
 
109
- This will generate and build a native extension named `arithmetic` in the current directory. A class named `Arithmetic` exposes the grammar parser. We can perform calculations now with `Arithmetic.new(expression).parse` which will return the result (or raise an exception if a syntax error is encountered).
130
+ Once we have that, we can compile our extension simply by running
131
+
132
+ $ rake compile
110
133
 
111
134
  ## TODO
135
+ * Add tests
136
+ * Support string/character literals in grammar rules
112
137
  * Automatically create Rakefile task
113
- * Change the parsers initialize() behavior (don't assume argument is file path)
114
-
115
- * Seems like you can hit EOF in the middle of action block and get wrong error msg
116
- * Benchmark -- what takes so long on the koa grammar?
117
138
  * Write to temp files, then move them into place
118
139
  * Move base module into the c extension and document the lexing helpers
140
+ * Seems like you can hit EOF in the middle of action block and get wrong error msg
141
+ * Benchmark -- what takes so long on the koa grammar?
119
142
 
120
- Not all Bison features are supported yet.
143
+ Not all Bison features are supported yet...
121
144
 
122
145
  ## Contributing
123
146
 
data/bin/mkbison CHANGED
@@ -17,6 +17,7 @@ def underscore(name)
17
17
  end
18
18
 
19
19
  options = OpenStruct.new
20
+ options.output = '.'
20
21
 
21
22
  opts = OptionParser.new do |opts|
22
23
  opts.banner += ' grammar_file'
@@ -39,7 +40,7 @@ options.name or abort(opts.help)
39
40
  options.output or abort(opts.help)
40
41
 
41
42
  begin
42
- bison = BisonParser.new(grammar_file).parse
43
+ bison = BisonParser.new(File.open(grammar_file, 'r')).parse
43
44
  rescue BisonParser::Error => e
44
45
  abort(e.message)
45
46
  end
@@ -72,6 +73,7 @@ Tempfile.new('bison-output.c').tap do |output|
72
73
  bison = Cocaine::CommandLine.new(ENV['BISON_PATH'] || 'bison', '-o :out :in')
73
74
  begin
74
75
  bison.run(in: bison_file.path, out: output.path)
76
+ warn bison.command_error_output
75
77
  rescue Cocaine::CommandNotFoundError => e
76
78
  warn("The bison command was not found on this system. " +
77
79
  "Make sure to install it before attempting rake compile.")
data/bison_parser.rby CHANGED
@@ -68,8 +68,7 @@ grammar_rule:
68
68
  ;
69
69
 
70
70
  components:
71
- { [] }
72
- | sequence[sequence]
71
+ sequence[sequence]
73
72
  { [sequence] }
74
73
  |
75
74
  components[sequences] PIPE sequence[sequence]
@@ -104,19 +103,7 @@ class BisonParser
104
103
  return Tokens::ACTIONS
105
104
  end
106
105
 
107
- # skip space
108
- while true
109
- while (c = self.read) && c =~ /\s/
110
- end
111
-
112
- if c == '#'
113
- while (char = self.read) && char != "\n"
114
- end
115
- else
116
- break
117
- end
118
- end
119
-
106
+ c = read_over_whitespace(line_comment_prefix: '#')
120
107
  return nil unless c
121
108
 
122
109
  case c
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'mkbison', path: '../..'
4
+ gem 'rake-compiler'
@@ -0,0 +1,5 @@
1
+ require "rake/extensiontask"
2
+
3
+ Rake::ExtensionTask.new "arithmetic" do |ext|
4
+ ext.lib_dir = "lib/arithmetic"
5
+ end
@@ -0,0 +1,80 @@
1
+ %token NUMBER
2
+ %token LPAREN
3
+ %token RPAREN
4
+
5
+ %left OP_PLUS OP_MINUS
6
+ %left OP_TIMES OP_OVER
7
+
8
+ %%
9
+
10
+ arithmetic:
11
+ expression[x]
12
+ { self.result = x }
13
+ ;
14
+
15
+ expression :
16
+ NUMBER
17
+ | OP_MINUS expression[x]
18
+ { -x }
19
+ | LPAREN expression[x] RPAREN
20
+ { x }
21
+ | addition
22
+ | subtraction
23
+ | multiplication
24
+ | division
25
+ ;
26
+
27
+ addition :
28
+ expression[left] OP_PLUS expression[right]
29
+ { left + right }
30
+ ;
31
+
32
+ subtraction :
33
+ expression[left] OP_MINUS expression[right]
34
+ { left - right }
35
+ ;
36
+
37
+ multiplication:
38
+ expression[left] OP_TIMES expression[right]
39
+ { left * right }
40
+ ;
41
+
42
+ division:
43
+ expression[left] OP_OVER expression[right]
44
+ { left / right }
45
+ ;
46
+
47
+ %%
48
+
49
+ class Arithmetic
50
+ def lex
51
+ c = read_over_whitespace
52
+ case c
53
+ when '0'..'9'
54
+ number = read_integer(c)
55
+ self.lex_value = number.to_i
56
+ return Tokens::NUMBER
57
+
58
+ when '+'
59
+ return Tokens::OP_PLUS
60
+
61
+ when '-'
62
+ return Tokens::OP_MINUS
63
+
64
+ when '*'
65
+ return Tokens::OP_TIMES
66
+
67
+ when '/'
68
+ return Tokens::OP_OVER
69
+
70
+ when '('
71
+ return Tokens::LPAREN
72
+
73
+ when ')'
74
+ return Tokens::RPAREN
75
+
76
+ else
77
+ return nil
78
+ end
79
+ end
80
+ end
@@ -403,7 +403,7 @@ union yyalloc
403
403
  /* YYNNTS -- Number of nonterminals. */
404
404
  #define YYNNTS 10
405
405
  /* YYNRULES -- Number of rules. */
406
- #define YYNRULES 22
406
+ #define YYNRULES 21
407
407
  /* YYNSTATES -- Number of states. */
408
408
  #define YYNSTATES 33
409
409
 
@@ -454,8 +454,8 @@ static const yytype_uint8 yytranslate[] =
454
454
  static const yytype_uint8 yyrline[] =
455
455
  {
456
456
  0, 37, 37, 50, 56, 67, 73, 84, 92, 96,
457
- 107, 115, 123, 135, 141, 152, 164, 170, 178, 190,
458
- 196, 205, 214
457
+ 107, 115, 123, 135, 141, 152, 163, 171, 183, 189,
458
+ 198, 207
459
459
  };
460
460
  #endif
461
461
 
@@ -482,12 +482,12 @@ static const yytype_uint16 yytoknum[] =
482
482
  };
483
483
  # endif
484
484
 
485
- #define YYPACT_NINF -8
485
+ #define YYPACT_NINF -9
486
486
 
487
487
  #define yypact_value_is_default(Yystate) \
488
- (!!((Yystate) == (-8)))
488
+ (!!((Yystate) == (-9)))
489
489
 
490
- #define YYTABLE_NINF -20
490
+ #define YYTABLE_NINF -1
491
491
 
492
492
  #define yytable_value_is_error(Yytable_value) \
493
493
  0
@@ -496,10 +496,10 @@ static const yytype_uint16 yytoknum[] =
496
496
  STATE-NUM. */
497
497
  static const yytype_int8 yypact[] =
498
498
  {
499
- -8, 10, -7, -8, -6, -8, 8, 12, 13, 14,
500
- 15, -1, -8, -8, -8, -8, -8, 16, 3, -8,
501
- -8, -3, -8, -4, -2, -8, -8, 17, -8, -2,
502
- 18, 11, -8
499
+ -9, 9, -8, -9, -7, -9, 7, 11, 12, 13,
500
+ 14, -2, -9, -9, -9, -9, -9, 6, 2, -9,
501
+ -9, -9, -9, -5, -3, -9, -9, 15, -9, -3,
502
+ 16, 17, -9
503
503
  };
504
504
 
505
505
  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -509,14 +509,14 @@ static const yytype_uint8 yydefact[] =
509
509
  {
510
510
  5, 0, 0, 1, 0, 13, 6, 8, 0, 0,
511
511
  0, 3, 9, 12, 7, 10, 11, 0, 0, 2,
512
- 14, 16, 4, 0, 17, 15, 19, 21, 20, 18,
513
- 0, 0, 22
512
+ 14, 18, 4, 0, 16, 15, 18, 20, 19, 17,
513
+ 0, 0, 21
514
514
  };
515
515
 
516
516
  /* YYPGOTO[NTERM-NUM]. */
517
517
  static const yytype_int8 yypgoto[] =
518
518
  {
519
- -8, -8, -8, -8, -8, -8, -8, -8, -8, 0
519
+ -9, -9, -9, -9, -9, -9, -9, -9, -9, -6
520
520
  };
521
521
 
522
522
  /* YYDEFGOTO[NTERM-NUM]. */
@@ -528,18 +528,18 @@ static const yytype_int8 yydefgoto[] =
528
528
  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
529
529
  positive, shift that token. If negative, reduce the rule whose
530
530
  number is the opposite. If YYTABLE_NINF, syntax error. */
531
- static const yytype_int8 yytable[] =
531
+ static const yytype_uint8 yytable[] =
532
532
  {
533
- -19, 27, 17, 25, 4, 5, 26, 8, 9, 10,
534
- 3, 18, 12, -19, 28, 13, 14, 15, 16, 22,
535
- 32, 31, 21, 0, 0, 30, 29
533
+ 27, 17, 25, 4, 5, 26, 8, 9, 10, 3,
534
+ 18, 12, 21, 28, 13, 14, 15, 16, 22, 31,
535
+ 29, 0, 0, 30, 0, 0, 32
536
536
  };
537
537
 
538
538
  static const yytype_int8 yycheck[] =
539
539
  {
540
- 3, 3, 3, 7, 11, 12, 10, 13, 14, 15,
541
- 0, 12, 4, 16, 16, 3, 3, 3, 3, 16,
542
- 9, 3, 6, -1, -1, 8, 26
540
+ 3, 3, 7, 11, 12, 10, 13, 14, 15, 0,
541
+ 12, 4, 6, 16, 3, 3, 3, 3, 16, 3,
542
+ 26, -1, -1, 8, -1, -1, 9
543
543
  };
544
544
 
545
545
  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
@@ -556,16 +556,16 @@ static const yytype_uint8 yystos[] =
556
556
  static const yytype_uint8 yyr1[] =
557
557
  {
558
558
  0, 17, 18, 19, 19, 20, 20, 21, 21, 21,
559
- 22, 22, 22, 23, 23, 24, 25, 25, 25, 26,
560
- 26, 26, 26
559
+ 22, 22, 22, 23, 23, 24, 25, 25, 26, 26,
560
+ 26, 26
561
561
  };
562
562
 
563
563
  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
564
564
  static const yytype_uint8 yyr2[] =
565
565
  {
566
566
  0, 2, 4, 0, 2, 0, 2, 3, 1, 2,
567
- 3, 3, 2, 0, 2, 4, 0, 1, 3, 0,
568
- 2, 2, 5
567
+ 3, 3, 2, 0, 2, 4, 1, 3, 0, 2,
568
+ 2, 5
569
569
  };
570
570
 
571
571
 
@@ -1486,65 +1486,56 @@ yyreduce:
1486
1486
  #line 164 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1487
1487
  {
1488
1488
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1489
- (yyval) = rb_funcall(__actions, rb_intern("_0_components_99914b932bd37a50b983c5e7c90ae93b"), 0);
1489
+ rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX((yylsp[0]).first_line), INT2FIX((yylsp[0]).first_column)));
1490
+ (yyval) = rb_funcall(__actions, rb_intern("_0_components_2403a823f1a9854a29da7cf64f191fbe"), 1, (yyvsp[0]));
1490
1491
  }
1491
- #line 1492 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1492
+ #line 1493 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1492
1493
  break;
1493
1494
 
1494
1495
  case 17:
1495
- #line 171 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1496
+ #line 172 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1496
1497
  {
1497
1498
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1499
+ rb_ivar_set(__actions, rb_intern("@sequences"), rb_ary_new3(2, INT2FIX((yylsp[-2]).first_line), INT2FIX((yylsp[-2]).first_column)));
1498
1500
  rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX((yylsp[0]).first_line), INT2FIX((yylsp[0]).first_column)));
1499
- (yyval) = rb_funcall(__actions, rb_intern("_1_components_2403a823f1a9854a29da7cf64f191fbe"), 1, (yyvsp[0]));
1501
+ (yyval) = rb_funcall(__actions, rb_intern("_1_components_62da044340939f02b6c0b52917617e17"), 2, (yyvsp[-2]), (yyvsp[0]));
1500
1502
  }
1501
- #line 1502 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1503
+ #line 1504 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1502
1504
  break;
1503
1505
 
1504
1506
  case 18:
1505
- #line 179 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1507
+ #line 183 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1506
1508
  {
1507
1509
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1508
- rb_ivar_set(__actions, rb_intern("@sequences"), rb_ary_new3(2, INT2FIX((yylsp[-2]).first_line), INT2FIX((yylsp[-2]).first_column)));
1509
- rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX((yylsp[0]).first_line), INT2FIX((yylsp[0]).first_column)));
1510
- (yyval) = rb_funcall(__actions, rb_intern("_2_components_62da044340939f02b6c0b52917617e17"), 2, (yyvsp[-2]), (yyvsp[0]));
1510
+ (yyval) = rb_funcall(__actions, rb_intern("_0_sequence_99914b932bd37a50b983c5e7c90ae93b"), 0);
1511
1511
  }
1512
1512
  #line 1513 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1513
1513
  break;
1514
1514
 
1515
1515
  case 19:
1516
1516
  #line 190 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1517
- {
1518
- rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1519
- (yyval) = rb_funcall(__actions, rb_intern("_0_sequence_99914b932bd37a50b983c5e7c90ae93b"), 0);
1520
- }
1521
- #line 1522 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1522
- break;
1523
-
1524
- case 20:
1525
- #line 197 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1526
1517
  {
1527
1518
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1528
1519
  rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX((yylsp[-1]).first_line), INT2FIX((yylsp[-1]).first_column)));
1529
1520
  rb_ivar_set(__actions, rb_intern("@code"), rb_ary_new3(2, INT2FIX((yylsp[0]).first_line), INT2FIX((yylsp[0]).first_column)));
1530
1521
  (yyval) = rb_funcall(__actions, rb_intern("_1_sequence_512ceffccf6bb7565046f90d6d7762ad"), 2, (yyvsp[-1]), (yyvsp[0]));
1531
1522
  }
1532
- #line 1533 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1523
+ #line 1524 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1533
1524
  break;
1534
1525
 
1535
- case 21:
1536
- #line 206 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1526
+ case 20:
1527
+ #line 199 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1537
1528
  {
1538
1529
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1539
1530
  rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX((yylsp[-1]).first_line), INT2FIX((yylsp[-1]).first_column)));
1540
1531
  rb_ivar_set(__actions, rb_intern("@follower"), rb_ary_new3(2, INT2FIX((yylsp[0]).first_line), INT2FIX((yylsp[0]).first_column)));
1541
1532
  (yyval) = rb_funcall(__actions, rb_intern("_2_sequence_4b2903c3aeb37d22d413a53653d0df28"), 2, (yyvsp[-1]), (yyvsp[0]));
1542
1533
  }
1543
- #line 1544 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1534
+ #line 1535 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1544
1535
  break;
1545
1536
 
1546
- case 22:
1547
- #line 215 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1537
+ case 21:
1538
+ #line 208 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1661 */
1548
1539
  {
1549
1540
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX((yyloc).first_line), INT2FIX((yyloc).first_column)));
1550
1541
  rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX((yylsp[-4]).first_line), INT2FIX((yylsp[-4]).first_column)));
@@ -1552,11 +1543,11 @@ yyreduce:
1552
1543
  rb_ivar_set(__actions, rb_intern("@tag"), rb_ary_new3(2, INT2FIX((yylsp[-1]).first_line), INT2FIX((yylsp[-1]).first_column)));
1553
1544
  (yyval) = rb_funcall(__actions, rb_intern("_3_sequence_68f2380aa0f3a7de0fb9b3482705a54c"), 3, (yyvsp[-4]), (yyvsp[-3]), (yyvsp[-1]));
1554
1545
  }
1555
- #line 1556 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1546
+ #line 1547 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1556
1547
  break;
1557
1548
 
1558
1549
 
1559
- #line 1560 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1550
+ #line 1551 "../../../../ext/bison_parser/bison_parser.c" /* yacc.c:1661 */
1560
1551
  default: break;
1561
1552
  }
1562
1553
  /* User semantic actions sometimes alter yychar, and that requires
@@ -1791,7 +1782,7 @@ yyreturn:
1791
1782
  #endif
1792
1783
  return yyresult;
1793
1784
  }
1794
- #line 226 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1906 */
1785
+ #line 219 "../../../../ext/bison_parser/bison_parser.y" /* yacc.c:1906 */
1795
1786
 
1796
1787
 
1797
1788
  static VALUE cBisonParser;
@@ -160,18 +160,11 @@ grammar_rule:
160
160
  ;
161
161
 
162
162
  components:
163
-
164
- {
165
- rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX(@$.first_line), INT2FIX(@$.first_column)));
166
- $$ = rb_funcall(__actions, rb_intern("_0_components_99914b932bd37a50b983c5e7c90ae93b"), 0);
167
- }
168
-
169
- |
170
163
  sequence
171
164
  {
172
165
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX(@$.first_line), INT2FIX(@$.first_column)));
173
166
  rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX(@1.first_line), INT2FIX(@1.first_column)));
174
- $$ = rb_funcall(__actions, rb_intern("_1_components_2403a823f1a9854a29da7cf64f191fbe"), 1, $1);
167
+ $$ = rb_funcall(__actions, rb_intern("_0_components_2403a823f1a9854a29da7cf64f191fbe"), 1, $1);
175
168
  }
176
169
 
177
170
  |
@@ -180,7 +173,7 @@ components:
180
173
  rb_ivar_set(__actions, rb_intern("@_"), rb_ary_new3(2, INT2FIX(@$.first_line), INT2FIX(@$.first_column)));
181
174
  rb_ivar_set(__actions, rb_intern("@sequences"), rb_ary_new3(2, INT2FIX(@1.first_line), INT2FIX(@1.first_column)));
182
175
  rb_ivar_set(__actions, rb_intern("@sequence"), rb_ary_new3(2, INT2FIX(@3.first_line), INT2FIX(@3.first_column)));
183
- $$ = rb_funcall(__actions, rb_intern("_2_components_62da044340939f02b6c0b52917617e17"), 2, $1, $3);
176
+ $$ = rb_funcall(__actions, rb_intern("_1_components_62da044340939f02b6c0b52917617e17"), 2, $1, $3);
184
177
  }
185
178
 
186
179
  ;
@@ -1,9 +1,14 @@
1
1
  require 'mkmf'
2
+ require 'shellwords'
2
3
 
3
4
  output = "#{File.dirname(__FILE__)}/bison_parser.c"
4
5
  bison_file = "#{File.dirname(__FILE__)}/bison_parser.y"
5
6
 
6
7
  bison = find_executable(ENV['BISON_PATH'] || 'bison')
8
+ bison_version = `#{Shellwords.shellescape(bison)} --version`.lines[0]
9
+ if bison_version =~ / [012]\.\d+(\.\d+)?\b/
10
+ abort("bison version must be >= 3.0.0")
11
+ end
7
12
  system(bison, '-o', output, bison_file) or abort("bison command failed")
8
13
 
9
14
  create_makefile 'bison_parser/bison_parser'
data/lib/bison/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Bison
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/bison_parser.rb CHANGED
@@ -12,19 +12,7 @@ class BisonParser
12
12
  return Tokens::ACTIONS
13
13
  end
14
14
 
15
- # skip space
16
- while true
17
- while (c = self.read) && c =~ /\s/
18
- end
19
-
20
- if c == '#'
21
- while (char = self.read) && char != "\n"
22
- end
23
- else
24
- break
25
- end
26
- end
27
-
15
+ c = read_over_whitespace(line_comment_prefix: '#')
28
16
  return nil unless c
29
17
 
30
18
  case c
@@ -54,15 +54,11 @@ class BisonParser
54
54
  Bison::Rule.new(name, components).tap{ |r| r.location = @name }
55
55
  end
56
56
 
57
- def _0_components_99914b932bd37a50b983c5e7c90ae93b()
58
- []
59
- end
60
-
61
- def _1_components_2403a823f1a9854a29da7cf64f191fbe(sequence)
57
+ def _0_components_2403a823f1a9854a29da7cf64f191fbe(sequence)
62
58
  [sequence]
63
59
  end
64
60
 
65
- def _2_components_62da044340939f02b6c0b52917617e17(sequences, sequence)
61
+ def _1_components_62da044340939f02b6c0b52917617e17(sequences, sequence)
66
62
  sequences << sequence
67
63
  end
68
64
 
@@ -1,3 +1,5 @@
1
+ require 'stringio'
2
+
1
3
  class BisonParser
2
4
  attr_reader :io
3
5
  attr_accessor :lex_value, :token_row, :token_col, :row, :col
@@ -6,12 +8,34 @@ class BisonParser
6
8
  module Base
7
9
  def initialize(io)
8
10
  if String === io
9
- io = ::File.open(io, 'r')
11
+ io = StringIO.new(io)
10
12
  end
11
13
  @source = io.respond_to?(:path) ? io.path : nil
12
14
  @io, @row, @col = io, 1, 0
13
15
  end
14
16
 
17
+ def read_over_whitespace(line_comment_prefix: nil)
18
+ while true
19
+ while (c = self.read) && c =~ /\s/
20
+ end
21
+
22
+ if line_comment_prefix && c == line_comment_prefix
23
+ while (char = self.read) && char != "\n"
24
+ end
25
+ else
26
+ break
27
+ end
28
+ end
29
+ c
30
+ end
31
+
32
+ def read_integer(number='')
33
+ while (c = self.peak) && ('0'..'9').include?(c)
34
+ number << self.read
35
+ end
36
+ number
37
+ end
38
+
15
39
  def read
16
40
  io.read(1).tap do |c|
17
41
  if c == "\n"
@@ -1,3 +1,5 @@
1
+ require 'stringio'
2
+
1
3
  class <%= name %>
2
4
  attr_reader :io
3
5
  attr_accessor :lex_value, :token_row, :token_col, :row, :col
@@ -6,12 +8,34 @@ class <%= name %>
6
8
  module Base
7
9
  def initialize(io)
8
10
  if String === io
9
- io = ::File.open(io, 'r')
11
+ io = StringIO.new(io)
10
12
  end
11
13
  @source = io.respond_to?(:path) ? io.path : nil
12
14
  @io, @row, @col = io, 1, 0
13
15
  end
14
16
 
17
+ def read_over_whitespace(line_comment_prefix: nil)
18
+ while true
19
+ while (c = self.read) && c =~ /\s/
20
+ end
21
+
22
+ if line_comment_prefix && c == line_comment_prefix
23
+ while (char = self.read) && char != "\n"
24
+ end
25
+ else
26
+ break
27
+ end
28
+ end
29
+ c
30
+ end
31
+
32
+ def read_integer(number='')
33
+ while (c = self.peak) && ('0'..'9').include?(c)
34
+ number << self.read
35
+ end
36
+ number
37
+ end
38
+
15
39
  def read
16
40
  io.read(1).tap do |c|
17
41
  if c == "\n"
@@ -1,9 +1,14 @@
1
1
  require 'mkmf'
2
+ require 'shellwords'
2
3
 
3
4
  output = "#{File.dirname(__FILE__)}/<%= uname %>.c"
4
5
  bison_file = "#{File.dirname(__FILE__)}/<%= uname %>.y"
5
6
 
6
7
  bison = find_executable(ENV['BISON_PATH'] || 'bison')
8
+ bison_version = `#{Shellwords.shellescape(bison)} --version`.lines[0]
9
+ if bison_version =~ / [012]\.\d+(\.\d+)?\b/
10
+ abort("bison version must be >= 3.0.0")
11
+ end
7
12
  system(bison, '-o', output, bison_file) or abort("bison command failed")
8
13
 
9
14
  create_makefile '<%= uname %>/<%= uname %>'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mkbison
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Woo
@@ -82,6 +82,9 @@ files:
82
82
  - Rakefile
83
83
  - bin/mkbison
84
84
  - bison_parser.rby
85
+ - examples/arithmetic/Gemfile
86
+ - examples/arithmetic/Rakefile
87
+ - examples/arithmetic/arithmetic.rby
85
88
  - ext/bison_parser/bison_parser.c
86
89
  - ext/bison_parser/bison_parser.y
87
90
  - ext/bison_parser/extconf.rb