depager 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +4 -0
- data/Manifest.txt +52 -0
- data/README.en +64 -0
- data/README.ja +128 -0
- data/bin/depager +47 -0
- data/data/depager/misc/depager-mode.el +209 -0
- data/data/depager/sample/extension/paction.dr +15 -0
- data/data/depager/sample/extension/pactiontest.dr +14 -0
- data/data/depager/sample/pl0d/pl0ds.dr +334 -0
- data/data/depager/sample/pl0d/pl0test.pl0 +34 -0
- data/data/depager/sample/sample_calc/calc.action.dr +33 -0
- data/data/depager/sample/sample_calc/calc.astdf.dr +54 -0
- data/data/depager/sample/sample_calc/calc.astl.action.dr +66 -0
- data/data/depager/sample/sample_calc/calc.astl.dr +55 -0
- data/data/depager/sample/sample_calc/calc.atree.dr +43 -0
- data/data/depager/sample/sample_calc/calc.cst.dr +45 -0
- data/data/depager/sample/sample_calc/calc.dr +43 -0
- data/data/depager/sample/sample_calc/calc.lex.dr +29 -0
- data/data/depager/sample/sample_calc/calc.nvaction.dr +33 -0
- data/data/depager/sample/sample_calc/calc_prec.nvaction.dr +31 -0
- data/data/depager/sample/slex_test/slextest1.dr +37 -0
- data/data/depager/sample/slex_test/slextest2.dr +33 -0
- data/lib/depager.rb +608 -0
- data/lib/depager/Rakefile +30 -0
- data/lib/depager/action.rb +47 -0
- data/lib/depager/ast_base.dr +232 -0
- data/lib/depager/ast_base.rb +1249 -0
- data/lib/depager/astdf.rb +10 -0
- data/lib/depager/astl.rb +14 -0
- data/lib/depager/atree.dr +55 -0
- data/lib/depager/atree.rb +336 -0
- data/lib/depager/cst.dr +182 -0
- data/lib/depager/cst.rb +625 -0
- data/lib/depager/lex.dr +76 -0
- data/lib/depager/lex.rb +306 -0
- data/lib/depager/lr.rb +604 -0
- data/lib/depager/nvaction.rb +21 -0
- data/lib/depager/parse_action.rb +24 -0
- data/lib/depager/parser.rb +248 -0
- data/lib/depager/psrtmpl.rb +33 -0
- data/lib/depager/slex.dr +161 -0
- data/lib/depager/slex.rb +646 -0
- data/lib/depager/srp.rb +50 -0
- data/lib/depager/template/astdf.erbs +57 -0
- data/lib/depager/template/astl.erbs +57 -0
- data/lib/depager/template/extension_lalr_master.erb +51 -0
- data/lib/depager/template/extension_lalr_slave.erb +107 -0
- data/lib/depager/template/simple.erb +21 -0
- data/lib/depager/template/single_lalr_parser.erb +97 -0
- data/lib/depager/utils.rb +355 -0
- data/lib/depager/version.rb +9 -0
- data/setup.rb +1585 -0
- metadata +103 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%extend ASTBuilderLazy ('depager/astl.rb')
|
4
|
+
%extend Action ('depager/action.rb')
|
5
|
+
%decorate @ASTBuilderLazy
|
6
|
+
%decorate @Action
|
7
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
8
|
+
%mixin Depager
|
9
|
+
%%
|
10
|
+
|
11
|
+
%LEX{
|
12
|
+
/\s+/, /\#.*/, /\n/ { }
|
13
|
+
/[1-9][0-9]*/ { yield _Token(:NUM, $&.to_i) }
|
14
|
+
/./ { yield _Token($&, $&) }
|
15
|
+
%}
|
16
|
+
|
17
|
+
%AST{
|
18
|
+
Node [value] { @value = nil }
|
19
|
+
Visitor { }
|
20
|
+
add(left, right) { ~value = visit(~left).value + visit(~right).value }
|
21
|
+
sub(left, right) { ~value = visit(~left).value - visit(~right).value }
|
22
|
+
mul(left, right) { ~value = visit(~left).value * visit(~right).value }
|
23
|
+
div(left, right) { ~value = visit(~left).value / visit(~right).value }
|
24
|
+
literal(-n) { ~value = ~n.value }
|
25
|
+
%}
|
26
|
+
|
27
|
+
#begin-rule
|
28
|
+
expr :
|
29
|
+
expr '+' term
|
30
|
+
=> add(expr, term)
|
31
|
+
{ val[0] + val[2] }
|
32
|
+
| expr '-' term
|
33
|
+
=> sub(expr, term)
|
34
|
+
{ val[0] - val[2] }
|
35
|
+
| term
|
36
|
+
=> term
|
37
|
+
{ val[0] }
|
38
|
+
;
|
39
|
+
term :
|
40
|
+
term '*' fact
|
41
|
+
=> mul(term, fact)
|
42
|
+
{ val[0] * val[2] }
|
43
|
+
| term '/' fact
|
44
|
+
=> div(term, fact)
|
45
|
+
{ val[0] / val[2] }
|
46
|
+
| fact
|
47
|
+
=> fact
|
48
|
+
{ val[0] }
|
49
|
+
;
|
50
|
+
fact :
|
51
|
+
NUM
|
52
|
+
=> literal(NUM)
|
53
|
+
{ val[0].value }
|
54
|
+
| '(' expr ')'
|
55
|
+
=> expr
|
56
|
+
{ val[1].value }
|
57
|
+
;
|
58
|
+
#end-rule
|
59
|
+
%%
|
60
|
+
require 'pp'
|
61
|
+
parser = createDecoratedTinyCalc()
|
62
|
+
t, r = parser.yyparse(STDIN)
|
63
|
+
v = Visitor.new
|
64
|
+
pp r
|
65
|
+
pp t.accept(v).value
|
66
|
+
pp t
|
@@ -0,0 +1,55 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%extend ASTBuilderLazy ('depager/astl.rb')
|
4
|
+
%decorate @ASTBuilderLazy
|
5
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
6
|
+
%%
|
7
|
+
|
8
|
+
%LEX{
|
9
|
+
/\s+/, /\#.*/ { }
|
10
|
+
/[1-9][0-9]*/ { yield _Token(:NUM, $&.to_i) }
|
11
|
+
/./ { yield _Token($&, $&) }
|
12
|
+
%}
|
13
|
+
|
14
|
+
%AST{
|
15
|
+
Node [value] { @value = nil }
|
16
|
+
Visitor { }
|
17
|
+
add(left, right) { ~value = visit(~left).value + visit(~right).value }
|
18
|
+
sub(left, right) { ~value = visit(~left).value - visit(~right).value }
|
19
|
+
mul(left, right) { ~value = visit(~left).value * visit(~right).value }
|
20
|
+
div(left, right) { ~value = visit(~left).value / visit(~right).value }
|
21
|
+
literal(-n) { ~value = ~n.value }
|
22
|
+
%}
|
23
|
+
|
24
|
+
#begin-rule
|
25
|
+
expr :
|
26
|
+
expr '+' term
|
27
|
+
=> add(expr, term)
|
28
|
+
| expr '-' term
|
29
|
+
=> sub(expr, term)
|
30
|
+
| term
|
31
|
+
=> term
|
32
|
+
;
|
33
|
+
term :
|
34
|
+
term '*' fact
|
35
|
+
=> mul(term, fact)
|
36
|
+
| term '/' fact
|
37
|
+
=> div(term, fact)
|
38
|
+
| fact
|
39
|
+
=> fact
|
40
|
+
;
|
41
|
+
fact :
|
42
|
+
NUM
|
43
|
+
=> literal(NUM)
|
44
|
+
| '(' expr ')'
|
45
|
+
=> expr
|
46
|
+
;
|
47
|
+
#end-rule
|
48
|
+
%%
|
49
|
+
require 'pp'
|
50
|
+
parser = createDecoratedTinyCalc()
|
51
|
+
t, = parser.yyparse(STDIN)
|
52
|
+
v = Visitor.new
|
53
|
+
pp t.accept(v).value
|
54
|
+
pp t
|
55
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%extend ATreeBuilder ('depager/atree.rb')
|
4
|
+
%decorate @ATreeBuilder
|
5
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
6
|
+
%mixin Depager
|
7
|
+
%%
|
8
|
+
|
9
|
+
%LEX{
|
10
|
+
/\s+/, /\#.*/ { }
|
11
|
+
/[1-9][0-9]*/ { yield _Token(:NUM, $&.to_i) }
|
12
|
+
/./ { yield _Token($&, $&) }
|
13
|
+
%}
|
14
|
+
|
15
|
+
#begin-rule
|
16
|
+
expr :
|
17
|
+
expr '+' term
|
18
|
+
=> add(expr, term)
|
19
|
+
| expr '-' term
|
20
|
+
=> sub(expr, term)
|
21
|
+
| term
|
22
|
+
=> term
|
23
|
+
;
|
24
|
+
term :
|
25
|
+
term '*' fact
|
26
|
+
=> mul(term, fact)
|
27
|
+
| term '/' fact
|
28
|
+
=> div(term, fact)
|
29
|
+
| fact
|
30
|
+
=> fact
|
31
|
+
;
|
32
|
+
fact :
|
33
|
+
NUM
|
34
|
+
=> literal(NUM)
|
35
|
+
| '(' expr ')'
|
36
|
+
=> expr
|
37
|
+
;
|
38
|
+
#end-rule
|
39
|
+
%%
|
40
|
+
require 'pp'
|
41
|
+
parser = createDecoratedTinyCalc
|
42
|
+
r, = parser.yyparse(STDIN)
|
43
|
+
pp r
|
@@ -0,0 +1,45 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%extend CSTBuilder ('depager/cst.rb')
|
4
|
+
%decorate @CSTBuilder
|
5
|
+
%%
|
6
|
+
|
7
|
+
%LEX{
|
8
|
+
/\s+/, /\#.*/ { }
|
9
|
+
/[1-9][0-9]*/ { yield _Token(:NUM, $&.to_i) }
|
10
|
+
/./ { yield _Token($&, $&) }
|
11
|
+
%}
|
12
|
+
|
13
|
+
%CST{
|
14
|
+
Node {
|
15
|
+
attr_accessor :value
|
16
|
+
def initialize
|
17
|
+
@value = nil
|
18
|
+
end
|
19
|
+
}
|
20
|
+
%}
|
21
|
+
|
22
|
+
#begin-rule
|
23
|
+
expr :
|
24
|
+
expr '+' term { ~value = ~expr.value + ~term.value }
|
25
|
+
| expr '-' term { ~value = ~expr.value + ~term.value }
|
26
|
+
| term { ~value = ~term.value }
|
27
|
+
;
|
28
|
+
term :
|
29
|
+
term '*' fact { ~value = ~term.value * ~fact.value }
|
30
|
+
| term '/' fact { ~value = ~term.value / ~fact.value }
|
31
|
+
| fact { ~value = ~fact.value }
|
32
|
+
;
|
33
|
+
fact :
|
34
|
+
NUM { ~value = ~num.value }
|
35
|
+
| '(' expr ')' { ~value = ~expr }
|
36
|
+
;
|
37
|
+
#end-rule
|
38
|
+
%%
|
39
|
+
require 'pp'
|
40
|
+
parser = createDecoratedTinyCalc
|
41
|
+
r, = parser.yyparse(STDIN)
|
42
|
+
v = Visitor.new
|
43
|
+
r.accept(v)
|
44
|
+
pp r
|
45
|
+
puts r.value
|
@@ -0,0 +1,43 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%inner{
|
3
|
+
def lex
|
4
|
+
until @file.eof?
|
5
|
+
@line = @file.gets
|
6
|
+
until @line.empty? do
|
7
|
+
case @line
|
8
|
+
when /\A\s+/, /\A\#.*/, /\A\n/
|
9
|
+
#skip blank and comment
|
10
|
+
when /\A[0-9]+/
|
11
|
+
yield :NUM, $&
|
12
|
+
when /\A./
|
13
|
+
yield $&, $&
|
14
|
+
else
|
15
|
+
raise RuntimeError, "must not happen #{line}"
|
16
|
+
end
|
17
|
+
@line = $'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
yield nil, nil
|
21
|
+
end
|
22
|
+
%}
|
23
|
+
%%
|
24
|
+
|
25
|
+
#begin-rule
|
26
|
+
expr :
|
27
|
+
expr '+' term
|
28
|
+
| expr '-' term
|
29
|
+
| term
|
30
|
+
;
|
31
|
+
term :
|
32
|
+
term '*' fact
|
33
|
+
| term '/' fact
|
34
|
+
| fact
|
35
|
+
;
|
36
|
+
fact :
|
37
|
+
NUM
|
38
|
+
| '(' expr ')'
|
39
|
+
;
|
40
|
+
#end-rule
|
41
|
+
%%
|
42
|
+
parser = TinyCalc.new()
|
43
|
+
parser.yyparse(STDIN)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%%
|
4
|
+
|
5
|
+
%LEX{
|
6
|
+
/\s+/, /\#.*/, /\n/ { }
|
7
|
+
/[1-9][0-9]*/ { yield :NUM, $&.to_i }
|
8
|
+
/./ { yield $&, $& }
|
9
|
+
%}
|
10
|
+
|
11
|
+
#begin-rule
|
12
|
+
expr :
|
13
|
+
expr '+' term
|
14
|
+
| expr '-' term
|
15
|
+
| term
|
16
|
+
;
|
17
|
+
term :
|
18
|
+
term '*' fact
|
19
|
+
| term '/' fact
|
20
|
+
| fact
|
21
|
+
;
|
22
|
+
fact :
|
23
|
+
NUM
|
24
|
+
| '(' expr ')'
|
25
|
+
;
|
26
|
+
#end-rule
|
27
|
+
%%
|
28
|
+
parser = TinyCalc.new()
|
29
|
+
parser.yyparse(STDIN)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%extend NVAction ('depager/nvaction.rb')
|
4
|
+
%decorate @NVAction
|
5
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
6
|
+
%%
|
7
|
+
|
8
|
+
%LEX{
|
9
|
+
/\s+/, /\#.*/ { }
|
10
|
+
/[1-9][0-9]*/ { yield _Token(:NUM, $&.to_i) }
|
11
|
+
/./ { yield _Token($&, $&) }
|
12
|
+
%}
|
13
|
+
|
14
|
+
#begin-rule
|
15
|
+
expr :
|
16
|
+
expr '+' term { _expr + _term }
|
17
|
+
| expr '-' term { _expr - _term }
|
18
|
+
| term { _term }
|
19
|
+
;
|
20
|
+
term :
|
21
|
+
term '*' fact { _term * _fact }
|
22
|
+
| term '/' fact { _term / _fact }
|
23
|
+
| fact { _fact }
|
24
|
+
;
|
25
|
+
fact :
|
26
|
+
NUM { _NUM.value }
|
27
|
+
| '(' expr ')' { _expr }
|
28
|
+
;
|
29
|
+
#end-rule
|
30
|
+
%%
|
31
|
+
parser = createDecoratedTinyCalc
|
32
|
+
r, = parser.yyparse(STDIN)
|
33
|
+
puts r
|
@@ -0,0 +1,31 @@
|
|
1
|
+
%class TinyCalc
|
2
|
+
%extend Lexer ('depager/lex.rb')
|
3
|
+
%extend NVAction ('depager/nvaction.rb')
|
4
|
+
%decorate @NVAction
|
5
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
6
|
+
%prec{
|
7
|
+
left '*' '/'
|
8
|
+
left '+' '-'
|
9
|
+
%}
|
10
|
+
%%
|
11
|
+
|
12
|
+
%LEX{
|
13
|
+
/\s+/, /\#.*/ { }
|
14
|
+
/[1-9][0-9]*/ { yield _Token(:NUM, $&.to_i) }
|
15
|
+
/./ { yield _Token($&, $&) }
|
16
|
+
%}
|
17
|
+
|
18
|
+
#begin-rule
|
19
|
+
expr :
|
20
|
+
expr-left '+' expr-right { _left + _right }
|
21
|
+
| expr-left '-' expr-right { _left - _right }
|
22
|
+
| expr-left '*' expr-right { _left * _right }
|
23
|
+
| expr-left '/' expr-right { _left / _right }
|
24
|
+
| '(' expr ')' { _expr }
|
25
|
+
| NUM { _NUM.value }
|
26
|
+
;
|
27
|
+
#end-rule
|
28
|
+
%%
|
29
|
+
parser = createDecoratedTinyCalc
|
30
|
+
r, = parser.yyparse(STDIN)
|
31
|
+
puts r
|
@@ -0,0 +1,37 @@
|
|
1
|
+
%class StatefulLexerTest1
|
2
|
+
%extend StatefulLexer ('depager/slex.rb')
|
3
|
+
%extend NVAction ('depager/nvaction.rb')
|
4
|
+
%decorate @NVAction
|
5
|
+
%decorate @StatefulLexer
|
6
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
7
|
+
%%
|
8
|
+
%LEX{
|
9
|
+
<START>
|
10
|
+
/\s+/ { }
|
11
|
+
/\/(([^\/\\]+|\\.)*)\// { yield _Token(:REGEXP, $&) }
|
12
|
+
/./ { yield _Token($&, $&) }
|
13
|
+
<OPMODE : START>
|
14
|
+
'\/' { yield _Token($&, $&) }
|
15
|
+
%}
|
16
|
+
|
17
|
+
#begin-rule
|
18
|
+
expr:
|
19
|
+
expr '/' [>:START] fact
|
20
|
+
| fact
|
21
|
+
;
|
22
|
+
fact:
|
23
|
+
REGEXP [>:OPMODE]
|
24
|
+
{ warn "REGEXP: #{_REGEXP.value}" }
|
25
|
+
;
|
26
|
+
#end-rule
|
27
|
+
%%
|
28
|
+
=begin
|
29
|
+
stmt:
|
30
|
+
TYPEDEF [>:TNMODE] TYPENAME ';' [>:START]
|
31
|
+
{ warn 'TYPEDEF:'+val[2] }
|
32
|
+
| ID ';'
|
33
|
+
{ warn 'ID:'+val[0] }
|
34
|
+
;
|
35
|
+
=end
|
36
|
+
p = createDecoratedStatefulLexerTest1
|
37
|
+
p.yyparse(STDIN)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
%class StatefulLexerTest2
|
2
|
+
%extend StatefulLexer ('depager/slex.rb')
|
3
|
+
%extend Action ('depager/action.rb')
|
4
|
+
%decorate @Action
|
5
|
+
%decorate @StatefulLexer
|
6
|
+
#%decorate ShiftReducePrinter ('depager/srp.rb')
|
7
|
+
%%
|
8
|
+
%LEX{
|
9
|
+
<START>
|
10
|
+
/[ \t]+/ { }
|
11
|
+
/\n/ { yield :EOL, $& }
|
12
|
+
/\w+/ { yield :ID, $& }
|
13
|
+
/./ { yield $&, $& }
|
14
|
+
<LCONT : START>
|
15
|
+
/\r?\n/ { }
|
16
|
+
%}
|
17
|
+
|
18
|
+
#begin-rule
|
19
|
+
stmts:
|
20
|
+
| stmts expr EOL
|
21
|
+
{ warn "<EOL>:#{@lexstate}" }
|
22
|
+
;
|
23
|
+
expr:
|
24
|
+
expr '+' [>:LCONT] fact
|
25
|
+
| fact
|
26
|
+
;
|
27
|
+
fact:
|
28
|
+
ID [>:START]
|
29
|
+
;
|
30
|
+
#end-rule
|
31
|
+
%%
|
32
|
+
p = createDecoratedStatefulLexerTest2
|
33
|
+
p.yyparse(STDIN)
|
data/lib/depager.rb
ADDED
@@ -0,0 +1,608 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require "depager/lr.rb"
|
3
|
+
require "depager/parser.rb"
|
4
|
+
require "depager/utils.rb"
|
5
|
+
|
6
|
+
module Depager
|
7
|
+
Tmpldir = "#{File.dirname(__FILE__)}/depager/template"
|
8
|
+
#
|
9
|
+
# file manager
|
10
|
+
#
|
11
|
+
class FileManager
|
12
|
+
def initialize f=nil
|
13
|
+
@file = f
|
14
|
+
end
|
15
|
+
def file= f
|
16
|
+
@file = f
|
17
|
+
end
|
18
|
+
def eof?
|
19
|
+
@file.eof?
|
20
|
+
end
|
21
|
+
# get file name
|
22
|
+
def fname
|
23
|
+
File.basename @file.path
|
24
|
+
end
|
25
|
+
# get file path
|
26
|
+
def path
|
27
|
+
@file.path
|
28
|
+
end
|
29
|
+
# get current line no
|
30
|
+
def lineno
|
31
|
+
@file.lineno
|
32
|
+
end
|
33
|
+
|
34
|
+
# get next line
|
35
|
+
def getline
|
36
|
+
@file.gets
|
37
|
+
end
|
38
|
+
alias gets getline
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# = declaration part parser
|
43
|
+
#
|
44
|
+
class DeclParser
|
45
|
+
attr_accessor :target_name, :g_parser, :generator
|
46
|
+
def initialize
|
47
|
+
end
|
48
|
+
def parse(file, fname=nil)
|
49
|
+
@files = FileManager.new(file)
|
50
|
+
@target_name = nil
|
51
|
+
|
52
|
+
parse_decl
|
53
|
+
end
|
54
|
+
def files
|
55
|
+
@files
|
56
|
+
end
|
57
|
+
alias file files
|
58
|
+
def parse_decl
|
59
|
+
until files.eof?
|
60
|
+
case line = files.getline
|
61
|
+
when /^\s*(#.*)?$/
|
62
|
+
#skip space and comment.
|
63
|
+
when /^%class\s*(\S+)\s*::\s*(\S+)\s*$/
|
64
|
+
@target_name = $1
|
65
|
+
@generator = Object.const_get("#{$2}Generator").new(self)
|
66
|
+
break
|
67
|
+
when /^%class\s*(.*?)\s*$/
|
68
|
+
@target_name = $1
|
69
|
+
@generator = SingleLALRParserGenerator.new(self)
|
70
|
+
break
|
71
|
+
when /^%defext\s*(.*?)\s*$/
|
72
|
+
@target_name = $1
|
73
|
+
@generator = ExtensionLALRParserGenerator.new(self)
|
74
|
+
break
|
75
|
+
else
|
76
|
+
error_exit "%class not found."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@generator.parse_decl
|
80
|
+
end
|
81
|
+
def error_exit msg, lineno=nil
|
82
|
+
lineno ||= files.lineno
|
83
|
+
warn "#{files.path}:#{lineno}: #{msg}"
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
def warning msg, lineno=nil
|
87
|
+
lineno ||= files.lineno
|
88
|
+
warn "#{files.path}:#{lineno}: warning: #{msg}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# = grammar part parser
|
94
|
+
#
|
95
|
+
class GrammarParser
|
96
|
+
include FileUtils
|
97
|
+
def files
|
98
|
+
@d_parser.files
|
99
|
+
end
|
100
|
+
alias file files
|
101
|
+
alias f files
|
102
|
+
|
103
|
+
private
|
104
|
+
GToken = Struct.new(:tag, :value, :name, :lineno)
|
105
|
+
def _GToken tag, value, name=nil
|
106
|
+
@token = GToken[tag, value, name, lineno]
|
107
|
+
return @token
|
108
|
+
end
|
109
|
+
def parse_grammar
|
110
|
+
gettoken
|
111
|
+
preRuleList
|
112
|
+
parse_rulelist
|
113
|
+
postRuleList
|
114
|
+
unless @token.tag == nil
|
115
|
+
error_exit "syntax error(grammar). "<<
|
116
|
+
"unexpected '#{@token.tag}' expect '%%'"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
def parse_rulelist
|
120
|
+
preRule
|
121
|
+
parse_rule
|
122
|
+
postRule
|
123
|
+
if @token.tag == :NT
|
124
|
+
parse_rulelist
|
125
|
+
end
|
126
|
+
end
|
127
|
+
def parse_rule
|
128
|
+
if @token[0] == :NT
|
129
|
+
@r = @token.value
|
130
|
+
gettoken
|
131
|
+
postLhs
|
132
|
+
|
133
|
+
unless @token.tag == ':'
|
134
|
+
error_exit "syntax error(grammar). "<<
|
135
|
+
"unexpected '#{@token.tag}' expecting ':'"
|
136
|
+
end
|
137
|
+
gettoken
|
138
|
+
|
139
|
+
@nrhs = 0
|
140
|
+
preRhsList
|
141
|
+
parse_rhslist
|
142
|
+
postRhsList
|
143
|
+
|
144
|
+
unless @token.tag == ';'
|
145
|
+
error_exit "syntax error(grammar). " <<
|
146
|
+
"unexpected '#{@token[0]}' expecting ';'"
|
147
|
+
end
|
148
|
+
gettoken
|
149
|
+
end
|
150
|
+
end
|
151
|
+
def parse_rhslist
|
152
|
+
@rhs = []; @rhsni = {}; @prec = nil
|
153
|
+
@optval = Array.new(@nparam)
|
154
|
+
preRhs
|
155
|
+
parse_syms
|
156
|
+
@g.push Rule[@r, @rhs, @prec]
|
157
|
+
postRhs
|
158
|
+
@nrhs += 1
|
159
|
+
|
160
|
+
if @token.tag == '|'
|
161
|
+
gettoken
|
162
|
+
parse_rhslist
|
163
|
+
end
|
164
|
+
end
|
165
|
+
def parse_syms
|
166
|
+
if @token.tag == :NT || @token.tag == :T
|
167
|
+
@rhs.push @token.value
|
168
|
+
@rhsni[@token.name] = @rhs.size - 1
|
169
|
+
|
170
|
+
gettoken
|
171
|
+
|
172
|
+
postSymbol
|
173
|
+
parse_syms
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
public
|
178
|
+
def gettoken
|
179
|
+
while !@line.empty? || !eof?
|
180
|
+
getline if @line.empty?
|
181
|
+
@oldline = @line
|
182
|
+
tag = s = name = val = nil
|
183
|
+
until @line.empty? do
|
184
|
+
case @line
|
185
|
+
when /^\s+/, /^\#.*/ ;
|
186
|
+
# skip
|
187
|
+
when /^=([a-zA-Z]+)/
|
188
|
+
@prec = s = $1.intern
|
189
|
+
val = @terms[s] ||= - (@terms.size + 1)
|
190
|
+
when /^([a-z][a-z0-9_]*)(-([a-z][_a-z0-9]*))?/
|
191
|
+
tag = :NT
|
192
|
+
s = $1.intern
|
193
|
+
name = $3 ? $3 : $1
|
194
|
+
val = @nonterms[s] ||= @nonterms.size
|
195
|
+
when /^([A-Z][A-Z0-9_]*)(-([a-z][_a-z0-9]*))?/
|
196
|
+
tag = :T
|
197
|
+
s = $1.intern
|
198
|
+
name = $3 ? $3 : $1
|
199
|
+
val = @terms[s] ||= - (@terms.size + 1)
|
200
|
+
when /^'(.)'/
|
201
|
+
tag = :T
|
202
|
+
name = s = $1
|
203
|
+
val = @terms[s] ||= - (@terms.size + 1)
|
204
|
+
when /^%%/
|
205
|
+
return _GToken(nil, nil)
|
206
|
+
when /^(.)/
|
207
|
+
tag = $&
|
208
|
+
val = $&
|
209
|
+
else
|
210
|
+
raise RuntimeError, "must not happen #{line}"
|
211
|
+
end
|
212
|
+
@oldline = @line; @line = $'
|
213
|
+
@i2s[val] ||= s if val && s
|
214
|
+
return _GToken(tag, val, name) if tag
|
215
|
+
end
|
216
|
+
end
|
217
|
+
return _GToken(nil, nil)
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
def preRuleList
|
222
|
+
@prerulelist.each{|o, m| o.send m}
|
223
|
+
end
|
224
|
+
def postRuleList
|
225
|
+
@postrulelist.each{|o, m| o.send m}
|
226
|
+
end
|
227
|
+
def preRule
|
228
|
+
@prerule.each{|o, m| o.send m}
|
229
|
+
end
|
230
|
+
def postRule
|
231
|
+
@postrule.each{|o, m| o.send m}
|
232
|
+
end
|
233
|
+
def postLhs
|
234
|
+
@postlhs.each{|o, m| o.send(m)}
|
235
|
+
end
|
236
|
+
def preRhsList
|
237
|
+
@prerhslist.each{|o, m| o.send m}
|
238
|
+
end
|
239
|
+
def postRhsList
|
240
|
+
@postrhslist.each{|o, m| o.send m}
|
241
|
+
end
|
242
|
+
def preRhs
|
243
|
+
@prerhs.each{|o, m| o.send(m)}
|
244
|
+
end
|
245
|
+
def postRhs
|
246
|
+
@postrhs.each{|o, m| o.send(m)}
|
247
|
+
end
|
248
|
+
def postSymbol
|
249
|
+
@postsymbol.each{|o, m| o.send(m)}
|
250
|
+
end
|
251
|
+
|
252
|
+
public
|
253
|
+
# get number of param
|
254
|
+
def getnparam a = nil
|
255
|
+
return nil unless a
|
256
|
+
return @nparams[a] if @nparams[a]
|
257
|
+
@nparam += 1
|
258
|
+
@nparams[a] = @nparam
|
259
|
+
end
|
260
|
+
|
261
|
+
def int_to_sym n
|
262
|
+
@i2s[n]
|
263
|
+
end
|
264
|
+
alias i2s int_to_sym
|
265
|
+
|
266
|
+
# add nonterm
|
267
|
+
# sym:: symbol
|
268
|
+
def add_nonterm sym
|
269
|
+
isym = @nonterms[sym] = @nonterms.size
|
270
|
+
@i2s[isym] = sym
|
271
|
+
return isym
|
272
|
+
end
|
273
|
+
|
274
|
+
# get rhs index by name
|
275
|
+
# name :: name
|
276
|
+
def name_to_rhs_index name
|
277
|
+
@rhsni[name]
|
278
|
+
end
|
279
|
+
alias n2ri name_to_rhs_index
|
280
|
+
|
281
|
+
# lhs value
|
282
|
+
def lhs
|
283
|
+
@r
|
284
|
+
end
|
285
|
+
|
286
|
+
attr_accessor :g, :terms, :nonterms, :precs
|
287
|
+
attr_accessor :line, :line0, :oldline, :token
|
288
|
+
attr_accessor :optouter, :optinner, :optmain, :mixin
|
289
|
+
attr_accessor :nparams, :rhs, :nrhs, :rhsni, :nparam, :r, :nrules
|
290
|
+
attr_accessor :prerulelist, :postrulelist, :prerule, :postrule
|
291
|
+
attr_accessor :postlhs, :prerhslist, :postrhslist
|
292
|
+
attr_accessor :prerhs, :postrhs, :postsymbol
|
293
|
+
attr_reader :d_parser, :target_name, :table
|
294
|
+
|
295
|
+
def initialize d_parser
|
296
|
+
@yydebug = true
|
297
|
+
@d_parser = d_parser
|
298
|
+
|
299
|
+
@i2s = {}
|
300
|
+
|
301
|
+
@g = [Rule[0 , [1]]]
|
302
|
+
@terms = { nil => -1, false => -2 }
|
303
|
+
@nonterms = {'$start' => 0}
|
304
|
+
|
305
|
+
@optinner = []
|
306
|
+
@optouter = []
|
307
|
+
@optmain = []
|
308
|
+
|
309
|
+
@nparams = {}
|
310
|
+
@nparam = 1
|
311
|
+
|
312
|
+
init_hook
|
313
|
+
end
|
314
|
+
|
315
|
+
private
|
316
|
+
# make grammar object
|
317
|
+
# precs:: prec infos
|
318
|
+
def make_grammar precs
|
319
|
+
ts = @nonterms.size - 1
|
320
|
+
@terms.each{|k,v| @terms[k] = ts - v}
|
321
|
+
@g[0].act = Array.new(@nparam-1)
|
322
|
+
|
323
|
+
@precs = {}
|
324
|
+
precs.each_with_index do |p, x|
|
325
|
+
p[1].each do |i|
|
326
|
+
if @terms[i]
|
327
|
+
@precs[@terms[i]] = [p[0], x]
|
328
|
+
else
|
329
|
+
error_exit "prec error '#{i}'."
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
@g.each do |rl|
|
334
|
+
rl.r.each_with_index{|i, x| rl.r[x] = ts - i if i < 0}
|
335
|
+
rl.prec &&= @precs[@terms[rl.prec]]
|
336
|
+
end
|
337
|
+
end
|
338
|
+
alias mkg make_grammar
|
339
|
+
|
340
|
+
# initialize hook
|
341
|
+
def init_hook
|
342
|
+
@prerulelist = []
|
343
|
+
@postrulelist = []
|
344
|
+
|
345
|
+
@prerule = []
|
346
|
+
@postrule = []
|
347
|
+
|
348
|
+
@postlhs = []
|
349
|
+
|
350
|
+
@postrhslist = []
|
351
|
+
@prerhslist = []
|
352
|
+
|
353
|
+
@prerhs = []
|
354
|
+
@postrhs = []
|
355
|
+
|
356
|
+
@postsymbol = []
|
357
|
+
end
|
358
|
+
|
359
|
+
public
|
360
|
+
# extend paser
|
361
|
+
# ext:: extension name
|
362
|
+
def extend_paser ext
|
363
|
+
init_hook
|
364
|
+
ext.each do |f,m|
|
365
|
+
require f if f
|
366
|
+
Object.const_get("#{m}Extension").new.send(:__regext__, self)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# get next line
|
371
|
+
def getline
|
372
|
+
@line0 = @line = files.getline
|
373
|
+
end
|
374
|
+
|
375
|
+
# parse and make LALR(1) table
|
376
|
+
# file:: input file object
|
377
|
+
# target_name:: class name
|
378
|
+
# mixin:: mixin modules
|
379
|
+
# precs:: prec infos
|
380
|
+
# return:: LALRTable
|
381
|
+
def parse(target_name = nil, mixin = [], precs = [])
|
382
|
+
@line = @line0 = ''
|
383
|
+
@oldline = nil
|
384
|
+
@target_name = target_name
|
385
|
+
@mixin = mixin
|
386
|
+
|
387
|
+
parse_grammar
|
388
|
+
make_grammar precs
|
389
|
+
@table = LALRTable.new(G.new(@g, @terms, @nonterms, @precs))
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
class Generator
|
394
|
+
include FileUtils
|
395
|
+
def files
|
396
|
+
@d_parser.files
|
397
|
+
end
|
398
|
+
Tmpldir = Depager::Tmpldir
|
399
|
+
Tmplfile = "**NOTHING**"
|
400
|
+
def tmpldir
|
401
|
+
Generator::Tmpldir
|
402
|
+
end
|
403
|
+
def tmplf
|
404
|
+
self.class::Tmplfile
|
405
|
+
end
|
406
|
+
attr_reader :d_parser
|
407
|
+
attr_accessor :optouter, :optinner, :optmain
|
408
|
+
attr_accessor :basis_name, :deco, :req
|
409
|
+
def initialize d_parser
|
410
|
+
@d_parser = d_parser
|
411
|
+
@basis_name = nil
|
412
|
+
|
413
|
+
@deco = []
|
414
|
+
@req = []
|
415
|
+
@ext = []
|
416
|
+
|
417
|
+
@optinner = []
|
418
|
+
@optouter = []
|
419
|
+
@optmain = []
|
420
|
+
@precs = []
|
421
|
+
@mixin = []
|
422
|
+
|
423
|
+
@tmpldir = Generator::Tmpldir+'/'
|
424
|
+
end
|
425
|
+
|
426
|
+
def parse_prec
|
427
|
+
precs = []
|
428
|
+
until files.eof?
|
429
|
+
case line = files.getline
|
430
|
+
when /^\s*(left|right|nonassoc)\s+(.*)$/
|
431
|
+
dir = $1.upcase.intern
|
432
|
+
syms = $2.split.map{|i| (i =~ /'(.)'/) ? $1 : i.intern}
|
433
|
+
precs.push [dir, syms]
|
434
|
+
when /^%\}\s*$/
|
435
|
+
break
|
436
|
+
else
|
437
|
+
error_exit "syntax error(prec).\n> #{line}"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
return precs
|
441
|
+
end
|
442
|
+
|
443
|
+
def parse_block
|
444
|
+
val = ''
|
445
|
+
until eof?
|
446
|
+
line = getline
|
447
|
+
break if line =~ /^%\}/
|
448
|
+
val.concat line
|
449
|
+
end
|
450
|
+
return val
|
451
|
+
end
|
452
|
+
|
453
|
+
def parse_common line
|
454
|
+
case line
|
455
|
+
when /^\s*(#.*)?$/
|
456
|
+
#skip space and comment.
|
457
|
+
when /^%decorate\s+(.*?)\s*\((.*)\)\s*$/
|
458
|
+
@deco.push $1
|
459
|
+
@req.push $2
|
460
|
+
when /^%decorate\s+(.*?)\s*$/
|
461
|
+
@deco.push $1
|
462
|
+
when /^%extend\s+(.*?)\s*\(\s*'(.*)'\s*\)\s*$/
|
463
|
+
@ext.push [$2.strip, $1] unless @ext.find{|i| i[1] == $1}
|
464
|
+
when /^%extend\s+(.*?)\s*$/
|
465
|
+
# not implemented yet
|
466
|
+
warning "'%extend E' is not implemented yet. " <<
|
467
|
+
"use '%extend Ext (filename)' instead.", lineno
|
468
|
+
when /^%require\s+'(.+?)'\s*$/
|
469
|
+
@req.push "'#{$1}'"
|
470
|
+
when /^%mixin\s+(\S+)\s*(\((.+)\)\s*)?$/
|
471
|
+
@mixin.push $1
|
472
|
+
@req.push $3 if $3
|
473
|
+
when /^%inner\s*\{\s*$/
|
474
|
+
@optinner.push parse_block
|
475
|
+
when /^%outer\s*\{\s*$/
|
476
|
+
# obsolete
|
477
|
+
warning "'%optouter' is obsolete."
|
478
|
+
@optouter.push parse_block
|
479
|
+
when /^%main\s*\{\s*$/
|
480
|
+
# obsolete
|
481
|
+
warning "'%optmain' is obsolete."
|
482
|
+
@optmain.push parse_block
|
483
|
+
when /^%prec\s*\{\s*$/
|
484
|
+
@precs = parse_prec
|
485
|
+
else
|
486
|
+
warning "syntax error(declaration).\n> #{line}"
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
class SimpleGenerator < Generator
|
492
|
+
Tmplfile = 'simple.erb'
|
493
|
+
def initialize d_parser
|
494
|
+
super
|
495
|
+
end
|
496
|
+
def out_code g_parser
|
497
|
+
ERB.new(File.read("#{tmpldir}/#{tmplf}")).result(binding)
|
498
|
+
end
|
499
|
+
def parse_decl
|
500
|
+
until eof?
|
501
|
+
line = getline
|
502
|
+
case line
|
503
|
+
when /^%%\s*$/
|
504
|
+
g_parser = GrammarParser.new(@d_parser)
|
505
|
+
g_parser.extend_paser @ext
|
506
|
+
g_parser.parse(@d_parser.target_name, @mixin, @precs)
|
507
|
+
@optmain.push parse_block
|
508
|
+
return out_code(g_parser)
|
509
|
+
else
|
510
|
+
parse_common(line)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
return nil
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
class SingleLALRParserGenerator < SimpleGenerator
|
518
|
+
Tmplfile = 'single_lalr_parser.erb'
|
519
|
+
def initialize d_parser
|
520
|
+
super
|
521
|
+
@basis_name = @d_parser.target_name + '.new()'
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
class ExtensionLALRParserGenerator < Generator
|
526
|
+
attr_accessor :regs, :paramkey
|
527
|
+
def initialize d_parser
|
528
|
+
super
|
529
|
+
@basis_name = 'self'
|
530
|
+
@paramkey = nil
|
531
|
+
@regs = {
|
532
|
+
"prerulelist" => nil,
|
533
|
+
"postrulelist" => nil,
|
534
|
+
|
535
|
+
"prerule" => nil,
|
536
|
+
"postrule" => nil,
|
537
|
+
|
538
|
+
"postlhs" => nil,
|
539
|
+
|
540
|
+
"postrhslist" => nil,
|
541
|
+
"prerhslist" => nil,
|
542
|
+
|
543
|
+
"prerhs" => nil,
|
544
|
+
"postrhs" => nil,
|
545
|
+
}
|
546
|
+
end
|
547
|
+
|
548
|
+
def parse_decl
|
549
|
+
until eof?
|
550
|
+
line = getline
|
551
|
+
case line
|
552
|
+
when /^%hook\s*(.*?)\s*$/
|
553
|
+
hs = $1
|
554
|
+
mtsk = if hs =~ /\/(([^\/\\]+|\\.)*)\/\s*(skip)?$/
|
555
|
+
hs = $`
|
556
|
+
[$1, $3 ? true : false]
|
557
|
+
end
|
558
|
+
hook = hs.split(' ')
|
559
|
+
hookname = hook.join('_')
|
560
|
+
hook.each{|i| @regs[i] = [i, "#{@d_parser.target_name}_#{hookname}"]}
|
561
|
+
@optouter.push(parse_hook(hookname, mtsk))
|
562
|
+
when /^%param\s*(.*)\s*$/
|
563
|
+
@paramkey = $1
|
564
|
+
else
|
565
|
+
parse_common(line)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
return out_master_code
|
569
|
+
end
|
570
|
+
|
571
|
+
def parse_hook hookname, mtsk
|
572
|
+
inner = ''
|
573
|
+
precs = []
|
574
|
+
banner = nil
|
575
|
+
mixin = [].concat(@mixin)
|
576
|
+
target_name = "#{@d_parser.target_name}_#{hookname}"
|
577
|
+
until eof?
|
578
|
+
line = getline
|
579
|
+
case line
|
580
|
+
when /^%banner\s*'(([^'\\]+|\\.)*)'\s*$/
|
581
|
+
banner = $1
|
582
|
+
when /^%inner\s*\{\s*$/
|
583
|
+
inner = parse_block
|
584
|
+
when /^%mixin\s*(.+?)\s*(\((.+)\)\s*)?$/
|
585
|
+
mixin.push $1
|
586
|
+
@req.push $3 if $3
|
587
|
+
when /^%prec\s*(.*?)\s*$/
|
588
|
+
precs = parse_prec
|
589
|
+
when /^%%\s*$/
|
590
|
+
g_parser = GrammarParser.new(@d_parser)
|
591
|
+
g_parser.extend_paser @ext
|
592
|
+
g_parser.parse(target_name, mixin, precs)
|
593
|
+
g_parser.optinner.push inner
|
594
|
+
return out_slave_code(g_parser, mixin, mtsk, banner)
|
595
|
+
else
|
596
|
+
warning "syntax error(declaration).\n> #{line}", lineno
|
597
|
+
end
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
def out_slave_code g_parser, mixin, mtsk, banner
|
602
|
+
ERB.new(File.read("#{tmpldir}/extension_lalr_slave.erb")).result(binding)
|
603
|
+
end
|
604
|
+
def out_master_code
|
605
|
+
ERB.new(File.read("#{tmpldir}/extension_lalr_master.erb")).result(binding)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|