class Astrapi::RubyGenerator

Constants

BASIC_TYPES

Astrapi basic types :

Attributes

mm[RW]

Public Instance Methods

code_for(klass) click to toggle source
# File lib/ruby_generator.rb, line 64
def code_for klass
  code=Code.new
  mother = klass.inheritance
  inheritance="< #{klass.inheritance}" if mother
  inheritance ||= "< Ast"
  code << "class #{klass.name} #{inheritance}"
  code.indent=2
  code << "attr_accessor #{klass.attrs.collect{|atr| ':'+atr.name.to_s}.join(',')}"
  code << "def initialize"
  code.indent=4
  klass.attrs.each do |atr|
    init=(atr.type.is_a? ArrayOf) ? "[]" : "nil"
    code << "@#{atr.name} = #{init}"
  end
  code.indent=2
  code << "end"
  code.indent=0
  code << "end"
  code
end
compute_possible_starters(type) click to toggle source
# File lib/ruby_generator.rb, line 321
def compute_possible_starters type
  ret=[]
  sons=find_sons_of(type)
  starters= (sons << type).flatten
  ret=starters.collect{|starter| starter.name.to_s.downcase.to_sym}
  ret=ret.collect{|sym| (BASIC_TYPES.include? sym) ? (sym.to_s+"_lit").to_sym : sym}
end
core_parsing_for(klass) click to toggle source
# File lib/ruby_generator.rb, line 243
def core_parsing_for klass
  kname=klass.name.to_s
  #puts "#{kname}".center(80,'=')
  v=kname.downcase
  attrs = hierarchical_attrs(klass) + klass.attrs #note the order!
  code=Code.new
  attrs.each{|attr| code << parsing_code_for(attr,v)}
  return code
end
find_mother_of(klass) click to toggle source
# File lib/ruby_generator.rb, line 262
def find_mother_of klass
  if id=klass.inheritance #identifier
    return mother=mm.classes.find{|k| k.name==id}
  end
  nil
end
find_sons_of(klass) click to toggle source
# File lib/ruby_generator.rb, line 269
def find_sons_of klass
  ret=mm.classes.select{|k| k.inheritance==klass.name}
  ret
end
generate(mm) click to toggle source
# File lib/ruby_generator.rb, line 13
def generate mm
  @mm=mm
  @path=__dir__ #ruby > 2.0
  @path+="/"
  generate_misc
  generate_ast
  generate_dot_ast_printer
  generate_sexp_lexer
  generate_sexp_parser
  generate_sexp_pretty_printer
  generate_visitor
  generate_dsl_compiler
end
generate_ast() click to toggle source
# File lib/ruby_generator.rb, line 27
def generate_ast
  puts "----> generating #{mm.name} DSL AST classes"
  mm_name=mm.name
  code = Code.new
  code << "# code generated automatically by M3E"
  code << "module #{mm_name}"
  code.newline
  code.indent=2
  code << "class Ast"
  code.indent=4
  code << "attr_accessor :comments_"
  code << "def accept(visitor, arg=nil)"
  code << "  name = self.class.name.split(/::/)[1]"
  code << "  visitor.send(\"visit\#{name}\".to_sym, self ,arg) # Metaprograming !"
  code << "end"
  code.newline
  code.indent=2
  code << "end"
  code.newline
  code.indent=2
  code << "class Comment_ < Ast"
  code.indent=4
  code << "attr_accessor :list"
  code.indent=2
  code << "end"
  code.newline
  code.indent=2

  mm.classes.each{|k|
    code << code_for(k)
    code.newline
  }
  code.indent=0
  code << "end"
  code.save_as("#{mm_name.to_s.downcase}_ast.rb",false)
end
generate_dot_ast_printer() click to toggle source
# File lib/ruby_generator.rb, line 218
def generate_dot_ast_printer
  puts "----> generating #{mm.name} DSL AST printer"
  #...... call templating engine
  engine = ERB.new(IO.read(@path+"template_ast_printer.rb"))
  printer_rb_code=engine.result(binding)
  File.open("#{mm.name.to_s.downcase}_ast_printer.rb",'w'){|f| f.puts printer_rb_code}
end
generate_dsl_compiler() click to toggle source
# File lib/ruby_generator.rb, line 226
def generate_dsl_compiler
  puts "----> generating #{mm.name} DSL compiler"
  #...... call templating engine
  engine = ERB.new(IO.read(@path+"template_compiler.rb"))
  code=engine.result(binding)
  File.open("#{mm.name.to_s.downcase}_compiler.rb",'w'){|f| f.puts code}
end
generate_misc() click to toggle source
# File lib/ruby_generator.rb, line 234
def generate_misc
  engine = ERB.new(IO.read(@path+"template_indent.rb"))
  code=engine.result(binding)
  File.open("indent.rb",'w'){|f| f.puts code}
  engine = ERB.new(IO.read(@path+"template_code.rb"))
  code=engine.result(binding)
  File.open("code.rb",'w'){|f| f.puts code}
end
generate_sexp_lexer() click to toggle source
# File lib/ruby_generator.rb, line 85
def generate_sexp_lexer
  puts "----> generating #{mm.name} DSL lexer"
  keywords=mm.classes.collect{|k| k.name.to_s}.map{|k| k.downcase.to_sym}
  regexp_code=Code.new #to be inserted in ERB template
  regexp_code.indent=4
  keywords.each do |kw|
    regexp_code << "#{kw.upcase.to_s.ljust(30)} = /\\A#{kw}/"
  end
  keywords_regexp=regexp_code.finalize
  #.......
  regexp_code=Code.new
  regexp_code.indent=8
  keywords.each do |kw|
    regexp_code << "when value = text.match(#{kw.upcase})"
    regexp_code << "  return Token.new [:#{kw},text,position]"
  end
  apply_regexp=regexp_code.finalize
  #...... call templating engine
  engine = ERB.new(IO.read(@path+"template_lexer.rb"))
  lexer_rb_code=engine.result(binding)
  File.open("#{mm.name.to_s.downcase}_lexer.rb",'w'){|f| f.puts lexer_rb_code}
end
generate_sexp_parser() click to toggle source
# File lib/ruby_generator.rb, line 108
def generate_sexp_parser
  puts "----> generating #{mm.name} DSL parser"
  code=Code.new
  code.indent=4
  mm.classes.each do |klass|
    kname=klass.name.to_s
    v=kname.downcase
    code.newline
    code << "def parse#{kname}"
    code.indent=6
    code << "indent \"#{klass.name}\""
    code << "if n=nil_maybe?"
    code << "  return n"
    code << "end"
    code << "#{v}_ = #{mm.name}::#{kname}.new"
    code << "#{v}_.comments_=parse_comments()"
    code << "expect :lparen"
    code << "expect :#{v}"
    code << core_parsing_for(klass)
    code << "expect :rparen"
    code << "dedent"
    code << "return #{v}_"
    code.indent=4
    code << "end"
  end
  parsing_methods=code.finalize
  #...... call templating engine
  engine = ERB.new(IO.read(@path+"template_parser.rb"))
  parser_rb_code=engine.result(binding)
  File.open("#{mm.name.to_s.downcase}_parser.rb",'w'){|f| f.puts parser_rb_code}
end
generate_sexp_pretty_printer() click to toggle source
# File lib/ruby_generator.rb, line 140
def generate_sexp_pretty_printer
  puts "----> generating #{mm.name} DSL pretty printer"
  code=Code.new
  code.indent=4
  mm.classes.each do |klass|
    kname=klass.name.to_s
    v=kname.downcase
    code.newline
    code << "def visit#{kname}(#{v}_,args=nil)"
    code.indent=6
    code << "indent \"#{klass.name}\""
    code << "ary=[]"
    code << "ary << #{v}_.comments_.val if #{v}_.comments_"
    code << "ary << #{v}_.class.to_s.downcase.split('::')[1].to_sym"
    attrs = hierarchical_attrs(klass) + klass.attrs #note the order!
    code_for_attrs=attrs.collect{|atr|
      if atr.type.is_a? ArrayOf
        e=((name=atr.name.to_s).end_with?('s')) ? name[0..-2] : "e"
        code << "#{v}_.#{atr.name}.each do |#{e}|"
        code.indent=8
        code << "ary << #{e}.accept(self,args)"
        code.indent=6
        code << "end"
      else
        code << "ary << #{v}_.#{atr.name}.accept(self)"
      end
    }
    code << "dedent"
    code << "return ary"
    code.indent=4
    code << "end"
  end
  visiting_methods=code.finalize
  #...... call templating engine
  engine = ERB.new(IO.read(@path+"template_pretty_printer.rb"))
  parser_rb_code=engine.result(binding)
  File.open("#{mm.name.to_s.downcase}_pp.rb",'w'){|f|
    f.puts parser_rb_code
  }
end
generate_visitor() click to toggle source
# File lib/ruby_generator.rb, line 181
def generate_visitor
  puts "----> generating #{mm.name} DSL visitor"
  code=Code.new
  code.indent=4
  mm.classes.each do |klass|
    kname=klass.name.to_s
    v=kname.downcase
    code.newline
    code << "def visit#{kname}(#{v}_,args=nil)"
    code.indent=6
    code << "indent \"#{klass.name}\""
    attrs = hierarchical_attrs(klass) + klass.attrs #note the order!
    code_for_attrs=attrs.collect{|atr|
      if atr.type.is_a? ArrayOf
        e=((name=atr.name.to_s).end_with?('s')) ? name[0..-2] : "e"
        code << "#{v}_.#{atr.name}.each do |#{e}|"
        code.indent=8
        code << "#{e}.accept(self,args)"
        code.indent=6
        code << "end"
      else
        code << "#{v}_.#{atr.name}.accept(self)"
      end
    }
    code << "dedent"
    code.indent=4
    code << "end"
  end
  visiting_methods=code.finalize
  #...... call templating engine
  engine = ERB.new(IO.read(@path+"template_visitor.rb"))
  parser_rb_code=engine.result(binding)
  File.open("#{mm.name.to_s.downcase}_visitor.rb",'w'){|f|
    f.puts parser_rb_code
  }
end
hierarchical_attrs(klass) click to toggle source
# File lib/ruby_generator.rb, line 253
def hierarchical_attrs klass
  inherited_attrs=[]
  if mother=find_mother_of(klass)
    inherited_attrs << mother.attrs
    inherited_attrs << hierarchical_attrs(mother)
  end
  return inherited_attrs.flatten
end
is_basic_type(type) click to toggle source
# File lib/ruby_generator.rb, line 317
def is_basic_type type
  type.name.to_s.upcase==type.name.to_s
end
parsing_code_for(attr,ast_node_var) click to toggle source
# File lib/ruby_generator.rb, line 274
def parsing_code_for attr,ast_node_var
  code=Code.new
  case type=attr.type
  when ArrayOf #note the position, before 'when Type' !
    possible_starters=compute_possible_starters(type.type)
    code << "comments=parse_comments()"
    lookahead=possible_starters.first.to_s.end_with?("_lit") ? 0 : 1
    starters=possible_starters.collect{|sym| ":#{sym}"}.join(',')
    code << "while showNext(#{lookahead}) && showNext(#{lookahead}).is_a?([#{starters}])"
    code.indent=2
    code << "case showNext(#{lookahead}).kind"
    possible_starters.each do |starter|
      code << "when :#{starter}"
      code.indent=4
      if starter.to_s.end_with?("_lit") #base types
        code << "#{ast_node_var}_.#{attr.name} << acceptIt"
      else
        code << "#{ast_node_var}_.#{attr.name} << parse#{starter.capitalize}()"
      end
      #code << "#{ast_node_var}_.#{attr.name} << parse#{starter.capitalize}()"
      code.indent=2
    end
    code << "else "
    code << "  abort \"ERROR when parsing attribute #{attr.name} : expecting one of [#{starters}].\n Got \#{showNext(1).kind}\""
    code << "end"
    code << "if #{ast_node_var}_.#{attr.name}.last.respond_to? :comments_"
    code.indent=4
    code << "#{ast_node_var}_.#{attr.name}.last.comments_=comments"
    code.indent=2
    code << "end"
    code << "comments=parse_comments()"
    code.indent=0
    code << "end"
  when Type
    if is_basic_type(type) #base types
      code << "#{ast_node_var}_.#{attr.name} = acceptIt"
    else
      code << "#{ast_node_var}_.#{attr.name} = parse#{type.name}()"
    end
  end
  return code
end