File: c_minus.rb

package info (click to toggle)
ruby-rsec 0.4.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 272 kB
  • sloc: ruby: 2,130; lisp: 13; makefile: 3
file content (175 lines) | stat: -rw-r--r-- 4,244 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
$:.unshift '../lib'
$:.unshift '../ext'
require "rsec"
require "pp"

module FixPP
  def pretty_print(q)
    q.group(1, sprintf("<%s", self.class.name[/\w+$/]), '>') {
      q.seplist(self.members, ->{}) {|member|
        q.breakable
        q.text member.to_s
        q.text '='
        q.group(1) {
          q.breakable ''
          q.pp self[member]
        }
      }
    }
  end
end

class CMinus
  include Rsec::Helpers
  extend Rsec::Helpers

  # node decls
  
  class Function < Struct.new :type, :id, :params, :body
    include FixPP
  end

  class Expr < Struct.new :expr
    include FixPP
  end

  class Block < Struct.new :var_decls, :statements
    include FixPP
  end

  class Call < Struct.new :function, :args
    include FixPP
  end

  class GetIndex < Struct.new :id, :idx
    include FixPP
  end
  
  # "terminal" rules
  
  NUM       = prim :unsigned_int64
  INT       = prim :int64
  NBSP      = /[\ \t]*/.r
  SPACE     = /\s*/.r
  ID        = /[a-zA-Z]\w*/.r 'id'
  TYPE      = (word('int') | word('void')).fail 'type'
  EOSTMT    = ';'.r 'end of statement'
  ELSE      = word('else').fail 'keyword_else'
  IF        = word('if').fail 'keyword_if'
  WHILE     = word('while').fail 'keyword_while'
  RETURN    = word('return').fail 'keyword_return'
  MUL_OP    = symbol(/[\*\/%]/)
  ADD_OP    = symbol(/[\+\-]/)
  COMP_OP   = symbol(/(\<=|\<|\>|\>=|==|!=)/).fail 'compare operator'
  COMMA     = /\s*,\s*/.r 'comma'
  EMPTY_BRA = /\[\s*\]/.r 'empty square bracket'

  # call(function apply) expression
  def call expr
    args = expr.join(COMMA).even
    seq_(ID, '(', args._?, ')') {
      |(id, _, args, _)|
      Call[id, *args]
    }
  end

  # (binary) expression
  def expression
    binary_arithmetic = lazy{factor}
      .join(MUL_OP).unbox
      .join(ADD_OP).unbox
      .join(COMP_OP).unbox
    expr = lazy{assign} | binary_arithmetic
    # abc
    # abc[12]
    var = seq_(ID, seq_('[', expr, ']')[1]._?) {
      |(id, (index))|
      index ? GetIndex[id, index] : id
    }
    assign = seq_(var, '=', expr)
    factor = seq_('(', expr, ')')[1] | call(expr) | var | INT
    # p expr.parse! "gcd (v ,u- u/v *v)"
    expr.map{|e| Expr[e] }
  end
    
  # statement parser builder, returns [stmt, block]
  def statement var_decl
    expr = expression()
    brace = seq_('(', expr, ')')[1]
    # statement
    _stmt = lazy{stmt} # to reduce the use of lazy{}
    
    expr_stmt = seq_(expr, EOSTMT)[0] | EOSTMT
    else_stmt = seq_(ELSE, _stmt)[1]
    if_stmt = seq_(IF, brace, _stmt, else_stmt._?)
    while_stmt = seq_(WHILE, brace, _stmt)
    return_stmt = seq_(RETURN, expr._?, EOSTMT){
      |(ret, maybe_expr)|
      [ret, *maybe_expr]
    }
    # { var_decls statements }
    block = seq('{', SPACE.join(var_decl).odd, SPACE.join(_stmt).odd, '}'){
      |(_, vars, stats, _)|
      Block[vars, stats]
    }
    stmt = block | if_stmt | while_stmt | return_stmt | expr_stmt
    # p if_stmt.parse! 'if(v == 0)return u;'
    [stmt, block]
  end
  
  def initialize
    type_id = seq_(TYPE, ID).cached
    # p type_id.parse! 'int a'
    
    var_decl = seq_(type_id, seq_('[', NUM, ']')[1]._?, EOSTMT){
      |(id, maybe_num)|
      [id, *maybe_num]
    }
    # p var_decl.parse! 'int a[12];'
    # p var_decl.parse! 'int a;'

    stmt, block = statement(var_decl)
    # p block.parse! "{int a;}"
    # p stmt.parse! 'if(3==2) {return 4;}'
    
    param = seq_(type_id, EMPTY_BRA._?) {
      |((ty, id), maybe_bra)|
      [ty, id, *maybe_bra]
    }
    params = param.join(COMMA).even | 'void'.r{[]}
    brace = seq_('(', params, ')')[1]
    fun_decl = seq_(type_id, brace, block){
      |(type, id), params, block|
      Function[type, id, params, block]
    }
    # p fun_decl.parse! 'int gcd(int u, int v){return 2;}'
    @program = SPACE.join(fun_decl | var_decl | EOSTMT).odd.eof
  end
  
  attr_reader :program
end

if __FILE__ == $PROGRAM_NAME
  c_minus = CMinus.new
  nodes = c_minus.program.parse! %Q[
    int gcd(int u, int v)
    {
      if (v == 0) return u;
      else return gcd(v,u-u / v*v);
    }

    void main(void)
    {
      int x; int y;
      while (1) {
        x = input();
        y = input();
        output(gcd(x ,y)) ;
      }
    }
  ]
  nodes.each do |node|
    pp node
  end
end