astrapi 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/astrapi +9 -0
- data/doc/astrapi.html +149 -0
- data/lib/ast.rb +68 -0
- data/lib/checker.rb +61 -0
- data/lib/class_diagram_printer.rb +83 -0
- data/lib/code.rb +48 -0
- data/lib/compiler.rb +149 -0
- data/lib/dbg.rb +7 -0
- data/lib/dot_printer.rb +72 -0
- data/lib/indent.rb +19 -0
- data/lib/lexer.rb +152 -0
- data/lib/parser.rb +110 -0
- data/lib/pretty_printer.rb +68 -0
- data/lib/ruby_generator.rb +329 -0
- data/lib/template_ast_printer.rb +79 -0
- data/lib/template_code.rb +48 -0
- data/lib/template_compiler.rb +59 -0
- data/lib/template_indent.rb +19 -0
- data/lib/template_lexer.rb +139 -0
- data/lib/template_parser.rb +75 -0
- data/lib/template_pretty_printer.rb +46 -0
- data/lib/template_visitor.rb +18 -0
- data/lib/version.rb +3 -0
- data/lib/visitor.rb +53 -0
- data/tests/geometry.mm +32 -0
- metadata +72 -0
data/lib/lexer.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'strscan'
|
3
|
+
#require 'benchmark'
|
4
|
+
|
5
|
+
class Token
|
6
|
+
attr_accessor :kind,:val,:pos
|
7
|
+
def initialize tab
|
8
|
+
@kind,@val,@pos=*tab
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_a? kind
|
12
|
+
case kind
|
13
|
+
when Symbol
|
14
|
+
return @kind==kind
|
15
|
+
when Array
|
16
|
+
for sym in kind
|
17
|
+
return true if @kind==sym
|
18
|
+
end
|
19
|
+
return false
|
20
|
+
else
|
21
|
+
raise "wrong type during lookahead"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Astrapi
|
27
|
+
|
28
|
+
class Lexer
|
29
|
+
|
30
|
+
# I like explicit things :
|
31
|
+
|
32
|
+
#................................................
|
33
|
+
IDENT = /\AIDENT/
|
34
|
+
INT = /\AINT/
|
35
|
+
FLOAT = /\AFLOAT/
|
36
|
+
STRING = /\ASTRING/
|
37
|
+
#................................................
|
38
|
+
MODULE = /\Amodule$/
|
39
|
+
CLASS = /\Aclass$/
|
40
|
+
END_ = /\Aend$/
|
41
|
+
ATTR = /\Aattr$/
|
42
|
+
#................................................
|
43
|
+
NEWLINE = /[\n]/
|
44
|
+
SPACE = /[ \t]+/
|
45
|
+
#.............punctuation........................
|
46
|
+
LPAREN = /\A\(/
|
47
|
+
RPAREN = /\A\)/
|
48
|
+
COMMENT = /\A\#(.*)/
|
49
|
+
#.............operators..........................
|
50
|
+
|
51
|
+
ARROW = /\A\=\>/
|
52
|
+
LBRACK = /\A\[/
|
53
|
+
RBRACK = /\A\]/
|
54
|
+
LT = /\A</
|
55
|
+
# .............literals.........................
|
56
|
+
IDENTIFIER = /\A[a-zA-Z]+[a-zA-Z_0-9]*/i
|
57
|
+
|
58
|
+
|
59
|
+
attr_accessor :suppress_comment
|
60
|
+
|
61
|
+
def initialize str=''
|
62
|
+
init(str)
|
63
|
+
@suppress_space=true
|
64
|
+
@suppress_comment=false
|
65
|
+
end
|
66
|
+
|
67
|
+
def init str
|
68
|
+
@ss=StringScanner.new(str)
|
69
|
+
@line=0
|
70
|
+
end
|
71
|
+
|
72
|
+
def tokenize str
|
73
|
+
@tokens=[]
|
74
|
+
init(str)
|
75
|
+
until @ss.eos?
|
76
|
+
@tokens << next_token()
|
77
|
+
end
|
78
|
+
return @tokens[0..-2]
|
79
|
+
end
|
80
|
+
|
81
|
+
#next token can detect spaces length
|
82
|
+
def next_token
|
83
|
+
|
84
|
+
if @ss.bol?
|
85
|
+
@line+=1
|
86
|
+
@old_pos=@ss.pos
|
87
|
+
end
|
88
|
+
|
89
|
+
position=[@line,@ss.pos-@old_pos+1]
|
90
|
+
|
91
|
+
return :eos if @ss.eos?
|
92
|
+
|
93
|
+
case
|
94
|
+
when text = @ss.scan(NEWLINE)
|
95
|
+
next_token()
|
96
|
+
when text = @ss.scan(SPACE)
|
97
|
+
next_token()
|
98
|
+
when text = @ss.scan(COMMENT)
|
99
|
+
next_token()
|
100
|
+
when text = @ss.scan(ARROW)
|
101
|
+
return Token.new [:arrow,text,position]
|
102
|
+
when text = @ss.scan(LT)
|
103
|
+
return Token.new [:lt,text,position]
|
104
|
+
when text = @ss.scan(LBRACK)
|
105
|
+
return Token.new [:lbrack,text,position]
|
106
|
+
when text = @ss.scan(RBRACK)
|
107
|
+
return Token.new [:rbrack,text,position]
|
108
|
+
when text = @ss.scan(IDENTIFIER)
|
109
|
+
case
|
110
|
+
when value = text.match(IDENT)
|
111
|
+
return Token.new [:IDENT,text,position]
|
112
|
+
when value = text.match(INT)
|
113
|
+
return Token.new [:INT,text,position]
|
114
|
+
when value = text.match(FLOAT)
|
115
|
+
return Token.new [:FLOAT,text,position]
|
116
|
+
when value = text.match(STRING)
|
117
|
+
return Token.new [:STRING,text,position]
|
118
|
+
when value = text.match(MODULE)
|
119
|
+
return Token.new [:module,text,position]
|
120
|
+
when value = text.match(CLASS)
|
121
|
+
return Token.new [:class,text,position]
|
122
|
+
when value = text.match(END_)
|
123
|
+
return Token.new [:end,text,position]
|
124
|
+
when value = text.match(ATTR)
|
125
|
+
return Token.new [:attr,text,position]
|
126
|
+
when value = text.match(LPAREN)
|
127
|
+
return Token.new [:lparen,text,position]
|
128
|
+
when value = text.match(RPAREN)
|
129
|
+
return Token.new [:rparen,text,position]
|
130
|
+
else
|
131
|
+
return Token.new [:identifier,text,position]
|
132
|
+
end
|
133
|
+
else
|
134
|
+
x = @ss.getch
|
135
|
+
return Token.new [x, x,position]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if $PROGRAM_NAME == __FILE__
|
142
|
+
str=IO.read(ARGV[0])
|
143
|
+
#str.downcase!
|
144
|
+
puts str
|
145
|
+
t1 = Time.now
|
146
|
+
lexer=Astrapi::Lexer.new
|
147
|
+
tokens=lexer.tokenize(str)
|
148
|
+
t2 = Time.now
|
149
|
+
pp tokens
|
150
|
+
puts "number of tokens : #{tokens.size}"
|
151
|
+
puts "tokenized in : #{t2-t1} s"
|
152
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require_relative 'lexer'
|
3
|
+
require_relative 'indent'
|
4
|
+
require_relative 'ast'
|
5
|
+
|
6
|
+
module Astrapi
|
7
|
+
|
8
|
+
class Parser
|
9
|
+
include Indent #helper methods mixed in
|
10
|
+
|
11
|
+
attr_accessor :lexer,:tokens
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@verbose=false
|
15
|
+
@lexer=Lexer.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse filename
|
19
|
+
str=IO.read(filename)
|
20
|
+
@tokens=lexer.tokenize(str)
|
21
|
+
@tokens=@tokens.select{|tok| tok.kind!=:comment}
|
22
|
+
pp @tokens if @verbose
|
23
|
+
parseModule
|
24
|
+
end
|
25
|
+
|
26
|
+
def acceptIt
|
27
|
+
tok=tokens.shift
|
28
|
+
say "consuming #{tok.val} (#{tok.kind})"
|
29
|
+
tok
|
30
|
+
end
|
31
|
+
|
32
|
+
def showNext
|
33
|
+
tokens.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def expect kind
|
37
|
+
if (actual=showNext.kind)!=kind
|
38
|
+
abort "ERROR at #{showNext.pos}. Expecting #{kind}. Got #{actual}"
|
39
|
+
else
|
40
|
+
return acceptIt()
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parseModule
|
45
|
+
indent "parseModule"
|
46
|
+
expect :module
|
47
|
+
name=Identifier.new(expect :identifier)
|
48
|
+
classes=[]
|
49
|
+
while showNext.is_a? :class
|
50
|
+
classes << parseClass
|
51
|
+
end
|
52
|
+
expect :end
|
53
|
+
dedent
|
54
|
+
return Astrapi::Module.new(name,classes)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parseClass
|
58
|
+
indent "parseClass"
|
59
|
+
expect :class
|
60
|
+
name=Identifier.new(expect :identifier)
|
61
|
+
if showNext.is_a? :lt
|
62
|
+
acceptIt
|
63
|
+
inheritance= Identifier.new(expect :identifier)
|
64
|
+
end
|
65
|
+
attrs=[]
|
66
|
+
while showNext.is_a? :attr
|
67
|
+
attrs << parseAttr
|
68
|
+
end
|
69
|
+
expect :end
|
70
|
+
dedent
|
71
|
+
return Astrapi::Klass.new(name,inheritance,attrs)
|
72
|
+
end
|
73
|
+
|
74
|
+
def parseAttr
|
75
|
+
indent "parseAttr"
|
76
|
+
expect :attr
|
77
|
+
name=Identifier.new(expect :identifier)
|
78
|
+
expect :arrow
|
79
|
+
type=parseAttrType
|
80
|
+
dedent
|
81
|
+
Attr.new(name,type)
|
82
|
+
end
|
83
|
+
|
84
|
+
def parseAttrType
|
85
|
+
indent "parseAttrType"
|
86
|
+
if showNext.is_a? [:IDENT,:FLOAT,:INT]
|
87
|
+
type=Type.new(Identifier.new(acceptIt))
|
88
|
+
else
|
89
|
+
type=Type.new(Identifier.new(expect :identifier))
|
90
|
+
end
|
91
|
+
if showNext.is_a? :lbrack
|
92
|
+
acceptIt
|
93
|
+
expect :rbrack
|
94
|
+
type=ArrayOf.new(type)
|
95
|
+
end
|
96
|
+
dedent
|
97
|
+
return type
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
if $PROGRAM_NAME == __FILE__
|
104
|
+
filename=ARGV[0]
|
105
|
+
raise "need a file !" if filename.nil?
|
106
|
+
t1 = Time.now
|
107
|
+
Astrapi::Parser.new.parse(filename)
|
108
|
+
t2 = Time.now
|
109
|
+
puts "parsed in : #{t2-t1} s"
|
110
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'ast'
|
2
|
+
require_relative 'indent'
|
3
|
+
require_relative 'code'
|
4
|
+
|
5
|
+
module Astrapi
|
6
|
+
|
7
|
+
class PrettyPrinter
|
8
|
+
|
9
|
+
include Indent
|
10
|
+
|
11
|
+
def print ast
|
12
|
+
ast.accept(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def visitModule modul,args=nil
|
16
|
+
indent "visitModule"
|
17
|
+
code=Code.new
|
18
|
+
code << "module #{name=modul.name}"
|
19
|
+
code.newline
|
20
|
+
code.indent=2
|
21
|
+
modul.classes.each{|k| code << k.accept(self) ; code.newline}
|
22
|
+
code.indent=0
|
23
|
+
code << "end"
|
24
|
+
dedent
|
25
|
+
return code
|
26
|
+
end
|
27
|
+
|
28
|
+
def visitKlass klass,args=nil
|
29
|
+
indent "visitKlass"
|
30
|
+
code = Code.new
|
31
|
+
inherit="< #{klass.inheritance}" if klass.inheritance
|
32
|
+
code << "class #{klass.name} #{inherit}"
|
33
|
+
code.indent=2
|
34
|
+
klass.attrs.each{|attr| code << attr.accept(self)}
|
35
|
+
code.indent=0
|
36
|
+
code << "end"
|
37
|
+
dedent
|
38
|
+
code
|
39
|
+
end
|
40
|
+
|
41
|
+
def visitAttr attr,args=nil
|
42
|
+
indent "visitAttr"
|
43
|
+
type=attr.type.accept(self)
|
44
|
+
str="attr #{attr.name} => #{type}"
|
45
|
+
dedent
|
46
|
+
str
|
47
|
+
end
|
48
|
+
|
49
|
+
def visitType type,args=nil
|
50
|
+
indent "visitType"
|
51
|
+
dedent
|
52
|
+
return type.name.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
def visitArrayOf arrayOf,args=nil
|
56
|
+
indent "visitArrayOf"
|
57
|
+
str="#{arrayOf.type.name}[]"
|
58
|
+
dedent
|
59
|
+
return str
|
60
|
+
end
|
61
|
+
|
62
|
+
def visitIdentifier id,args=nil
|
63
|
+
indent "visitIdentifier"
|
64
|
+
dedent
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require_relative 'ast'
|
3
|
+
|
4
|
+
module Astrapi
|
5
|
+
|
6
|
+
class RubyGenerator
|
7
|
+
|
8
|
+
attr_accessor :mm
|
9
|
+
|
10
|
+
#Astrapi basic types :
|
11
|
+
BASIC_TYPES = [:integer,:float,:range,:ident,:string]
|
12
|
+
|
13
|
+
def generate mm
|
14
|
+
@mm=mm
|
15
|
+
@path=__dir__ #ruby > 2.0
|
16
|
+
@path+="/"
|
17
|
+
generate_misc
|
18
|
+
generate_ast
|
19
|
+
generate_dot_ast_printer
|
20
|
+
generate_sexp_lexer
|
21
|
+
generate_sexp_parser
|
22
|
+
generate_sexp_pretty_printer
|
23
|
+
generate_visitor
|
24
|
+
generate_dsl_compiler
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_ast
|
28
|
+
puts "----> generating #{mm.name} DSL AST classes"
|
29
|
+
mm_name=mm.name
|
30
|
+
code = Code.new
|
31
|
+
code << "# code generated automatically by M3E"
|
32
|
+
code << "module #{mm_name}"
|
33
|
+
code.newline
|
34
|
+
code.indent=2
|
35
|
+
code << "class Ast"
|
36
|
+
code.indent=4
|
37
|
+
code << "attr_accessor :comments_"
|
38
|
+
code << "def accept(visitor, arg=nil)"
|
39
|
+
code << " name = self.class.name.split(/::/)[1]"
|
40
|
+
code << " visitor.send(\"visit\#{name}\".to_sym, self ,arg) # Metaprograming !"
|
41
|
+
code << "end"
|
42
|
+
code.newline
|
43
|
+
code.indent=2
|
44
|
+
code << "end"
|
45
|
+
code.newline
|
46
|
+
code.indent=2
|
47
|
+
code << "class Comment_ < Ast"
|
48
|
+
code.indent=4
|
49
|
+
code << "attr_accessor :list"
|
50
|
+
code.indent=2
|
51
|
+
code << "end"
|
52
|
+
code.newline
|
53
|
+
code.indent=2
|
54
|
+
|
55
|
+
mm.classes.each{|k|
|
56
|
+
code << code_for(k)
|
57
|
+
code.newline
|
58
|
+
}
|
59
|
+
code.indent=0
|
60
|
+
code << "end"
|
61
|
+
code.save_as("#{mm_name.to_s.downcase}_ast.rb",false)
|
62
|
+
end
|
63
|
+
|
64
|
+
def code_for klass
|
65
|
+
code=Code.new
|
66
|
+
mother = klass.inheritance
|
67
|
+
inheritance="< #{klass.inheritance}" if mother
|
68
|
+
inheritance ||= "< Ast"
|
69
|
+
code << "class #{klass.name} #{inheritance}"
|
70
|
+
code.indent=2
|
71
|
+
code << "attr_accessor #{klass.attrs.collect{|atr| ':'+atr.name.to_s}.join(',')}"
|
72
|
+
code << "def initialize"
|
73
|
+
code.indent=4
|
74
|
+
klass.attrs.each do |atr|
|
75
|
+
init=(atr.type.is_a? ArrayOf) ? "[]" : "nil"
|
76
|
+
code << "@#{atr.name} = #{init}"
|
77
|
+
end
|
78
|
+
code.indent=2
|
79
|
+
code << "end"
|
80
|
+
code.indent=0
|
81
|
+
code << "end"
|
82
|
+
code
|
83
|
+
end
|
84
|
+
|
85
|
+
def generate_sexp_lexer
|
86
|
+
puts "----> generating #{mm.name} DSL lexer"
|
87
|
+
keywords=mm.classes.collect{|k| k.name.to_s}.map{|k| k.downcase.to_sym}
|
88
|
+
regexp_code=Code.new #to be inserted in ERB template
|
89
|
+
regexp_code.indent=4
|
90
|
+
keywords.each do |kw|
|
91
|
+
regexp_code << "#{kw.upcase.to_s.ljust(30)} = /\\A#{kw}/"
|
92
|
+
end
|
93
|
+
keywords_regexp=regexp_code.finalize
|
94
|
+
#.......
|
95
|
+
regexp_code=Code.new
|
96
|
+
regexp_code.indent=8
|
97
|
+
keywords.each do |kw|
|
98
|
+
regexp_code << "when value = text.match(#{kw.upcase})"
|
99
|
+
regexp_code << " return Token.new [:#{kw},text,position]"
|
100
|
+
end
|
101
|
+
apply_regexp=regexp_code.finalize
|
102
|
+
#...... call templating engine
|
103
|
+
engine = ERB.new(IO.read(@path+"template_lexer.rb"))
|
104
|
+
lexer_rb_code=engine.result(binding)
|
105
|
+
File.open("#{mm.name.to_s.downcase}_lexer.rb",'w'){|f| f.puts lexer_rb_code}
|
106
|
+
end
|
107
|
+
|
108
|
+
def generate_sexp_parser
|
109
|
+
puts "----> generating #{mm.name} DSL parser"
|
110
|
+
code=Code.new
|
111
|
+
code.indent=4
|
112
|
+
mm.classes.each do |klass|
|
113
|
+
kname=klass.name.to_s
|
114
|
+
v=kname.downcase
|
115
|
+
code.newline
|
116
|
+
code << "def parse#{kname}"
|
117
|
+
code.indent=6
|
118
|
+
code << "indent \"#{klass.name}\""
|
119
|
+
code << "if n=nil_maybe?"
|
120
|
+
code << " return n"
|
121
|
+
code << "end"
|
122
|
+
code << "#{v}_ = #{mm.name}::#{kname}.new"
|
123
|
+
code << "#{v}_.comments_=parse_comments()"
|
124
|
+
code << "expect :lparen"
|
125
|
+
code << "expect :#{v}"
|
126
|
+
code << core_parsing_for(klass)
|
127
|
+
code << "expect :rparen"
|
128
|
+
code << "dedent"
|
129
|
+
code << "return #{v}_"
|
130
|
+
code.indent=4
|
131
|
+
code << "end"
|
132
|
+
end
|
133
|
+
parsing_methods=code.finalize
|
134
|
+
#...... call templating engine
|
135
|
+
engine = ERB.new(IO.read(@path+"template_parser.rb"))
|
136
|
+
parser_rb_code=engine.result(binding)
|
137
|
+
File.open("#{mm.name.to_s.downcase}_parser.rb",'w'){|f| f.puts parser_rb_code}
|
138
|
+
end
|
139
|
+
|
140
|
+
def generate_sexp_pretty_printer
|
141
|
+
puts "----> generating #{mm.name} DSL pretty printer"
|
142
|
+
code=Code.new
|
143
|
+
code.indent=4
|
144
|
+
mm.classes.each do |klass|
|
145
|
+
kname=klass.name.to_s
|
146
|
+
v=kname.downcase
|
147
|
+
code.newline
|
148
|
+
code << "def visit#{kname}(#{v}_,args=nil)"
|
149
|
+
code.indent=6
|
150
|
+
code << "indent \"#{klass.name}\""
|
151
|
+
code << "ary=[]"
|
152
|
+
code << "ary << #{v}_.comments_.val if #{v}_.comments_"
|
153
|
+
code << "ary << #{v}_.class.to_s.downcase.split('::')[1].to_sym"
|
154
|
+
attrs = hierarchical_attrs(klass) + klass.attrs #note the order!
|
155
|
+
code_for_attrs=attrs.collect{|atr|
|
156
|
+
if atr.type.is_a? ArrayOf
|
157
|
+
e=((name=atr.name.to_s).end_with?('s')) ? name[0..-2] : "e"
|
158
|
+
code << "#{v}_.#{atr.name}.each do |#{e}|"
|
159
|
+
code.indent=8
|
160
|
+
code << "ary << #{e}.accept(self,args)"
|
161
|
+
code.indent=6
|
162
|
+
code << "end"
|
163
|
+
else
|
164
|
+
code << "ary << #{v}_.#{atr.name}.accept(self)"
|
165
|
+
end
|
166
|
+
}
|
167
|
+
code << "dedent"
|
168
|
+
code << "return ary"
|
169
|
+
code.indent=4
|
170
|
+
code << "end"
|
171
|
+
end
|
172
|
+
visiting_methods=code.finalize
|
173
|
+
#...... call templating engine
|
174
|
+
engine = ERB.new(IO.read(@path+"template_pretty_printer.rb"))
|
175
|
+
parser_rb_code=engine.result(binding)
|
176
|
+
File.open("#{mm.name.to_s.downcase}_pp.rb",'w'){|f|
|
177
|
+
f.puts parser_rb_code
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
def generate_visitor
|
182
|
+
puts "----> generating #{mm.name} DSL visitor"
|
183
|
+
code=Code.new
|
184
|
+
code.indent=4
|
185
|
+
mm.classes.each do |klass|
|
186
|
+
kname=klass.name.to_s
|
187
|
+
v=kname.downcase
|
188
|
+
code.newline
|
189
|
+
code << "def visit#{kname}(#{v}_,args=nil)"
|
190
|
+
code.indent=6
|
191
|
+
code << "indent \"#{klass.name}\""
|
192
|
+
attrs = hierarchical_attrs(klass) + klass.attrs #note the order!
|
193
|
+
code_for_attrs=attrs.collect{|atr|
|
194
|
+
if atr.type.is_a? ArrayOf
|
195
|
+
e=((name=atr.name.to_s).end_with?('s')) ? name[0..-2] : "e"
|
196
|
+
code << "#{v}_.#{atr.name}.each do |#{e}|"
|
197
|
+
code.indent=8
|
198
|
+
code << "#{e}.accept(self,args)"
|
199
|
+
code.indent=6
|
200
|
+
code << "end"
|
201
|
+
else
|
202
|
+
code << "#{v}_.#{atr.name}.accept(self)"
|
203
|
+
end
|
204
|
+
}
|
205
|
+
code << "dedent"
|
206
|
+
code.indent=4
|
207
|
+
code << "end"
|
208
|
+
end
|
209
|
+
visiting_methods=code.finalize
|
210
|
+
#...... call templating engine
|
211
|
+
engine = ERB.new(IO.read(@path+"template_visitor.rb"))
|
212
|
+
parser_rb_code=engine.result(binding)
|
213
|
+
File.open("#{mm.name.to_s.downcase}_visitor.rb",'w'){|f|
|
214
|
+
f.puts parser_rb_code
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
def generate_dot_ast_printer
|
219
|
+
puts "----> generating #{mm.name} DSL AST printer"
|
220
|
+
#...... call templating engine
|
221
|
+
engine = ERB.new(IO.read(@path+"template_ast_printer.rb"))
|
222
|
+
printer_rb_code=engine.result(binding)
|
223
|
+
File.open("#{mm.name.to_s.downcase}_ast_printer.rb",'w'){|f| f.puts printer_rb_code}
|
224
|
+
end
|
225
|
+
|
226
|
+
def generate_dsl_compiler
|
227
|
+
puts "----> generating #{mm.name} DSL compiler"
|
228
|
+
#...... call templating engine
|
229
|
+
engine = ERB.new(IO.read(@path+"template_compiler.rb"))
|
230
|
+
code=engine.result(binding)
|
231
|
+
File.open("#{mm.name.to_s.downcase}_compiler.rb",'w'){|f| f.puts code}
|
232
|
+
end
|
233
|
+
|
234
|
+
def generate_misc
|
235
|
+
engine = ERB.new(IO.read(@path+"template_indent.rb"))
|
236
|
+
code=engine.result(binding)
|
237
|
+
File.open("indent.rb",'w'){|f| f.puts code}
|
238
|
+
engine = ERB.new(IO.read(@path+"template_code.rb"))
|
239
|
+
code=engine.result(binding)
|
240
|
+
File.open("code.rb",'w'){|f| f.puts code}
|
241
|
+
end
|
242
|
+
|
243
|
+
def core_parsing_for klass
|
244
|
+
kname=klass.name.to_s
|
245
|
+
#puts "#{kname}".center(80,'=')
|
246
|
+
v=kname.downcase
|
247
|
+
attrs = hierarchical_attrs(klass) + klass.attrs #note the order!
|
248
|
+
code=Code.new
|
249
|
+
attrs.each{|attr| code << parsing_code_for(attr,v)}
|
250
|
+
return code
|
251
|
+
end
|
252
|
+
|
253
|
+
def hierarchical_attrs klass
|
254
|
+
inherited_attrs=[]
|
255
|
+
if mother=find_mother_of(klass)
|
256
|
+
inherited_attrs << mother.attrs
|
257
|
+
inherited_attrs << hierarchical_attrs(mother)
|
258
|
+
end
|
259
|
+
return inherited_attrs.flatten
|
260
|
+
end
|
261
|
+
|
262
|
+
def find_mother_of klass
|
263
|
+
if id=klass.inheritance #identifier
|
264
|
+
return mother=mm.classes.find{|k| k.name==id}
|
265
|
+
end
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
def find_sons_of klass
|
270
|
+
ret=mm.classes.select{|k| k.inheritance==klass.name}
|
271
|
+
ret
|
272
|
+
end
|
273
|
+
|
274
|
+
def parsing_code_for attr,ast_node_var
|
275
|
+
code=Code.new
|
276
|
+
case type=attr.type
|
277
|
+
when ArrayOf #note the position, before 'when Type' !
|
278
|
+
possible_starters=compute_possible_starters(type.type)
|
279
|
+
code << "comments=parse_comments()"
|
280
|
+
lookahead=possible_starters.first.to_s.end_with?("_lit") ? 0 : 1
|
281
|
+
starters=possible_starters.collect{|sym| ":#{sym}"}.join(',')
|
282
|
+
code << "while showNext(#{lookahead}) && showNext(#{lookahead}).is_a?([#{starters}])"
|
283
|
+
code.indent=2
|
284
|
+
code << "case showNext(#{lookahead}).kind"
|
285
|
+
possible_starters.each do |starter|
|
286
|
+
code << "when :#{starter}"
|
287
|
+
code.indent=4
|
288
|
+
if starter.to_s.end_with?("_lit") #base types
|
289
|
+
code << "#{ast_node_var}_.#{attr.name} << acceptIt"
|
290
|
+
else
|
291
|
+
code << "#{ast_node_var}_.#{attr.name} << parse#{starter.capitalize}()"
|
292
|
+
end
|
293
|
+
#code << "#{ast_node_var}_.#{attr.name} << parse#{starter.capitalize}()"
|
294
|
+
code.indent=2
|
295
|
+
end
|
296
|
+
code << "else "
|
297
|
+
code << " abort \"ERROR when parsing attribute #{attr.name} : expecting one of [#{starters}].\n Got \#{showNext(1).kind}\""
|
298
|
+
code << "end"
|
299
|
+
code << "if #{ast_node_var}_.#{attr.name}.last.respond_to? :comments_"
|
300
|
+
code.indent=4
|
301
|
+
code << "#{ast_node_var}_.#{attr.name}.last.comments_=comments"
|
302
|
+
code.indent=2
|
303
|
+
code << "end"
|
304
|
+
code << "comments=parse_comments()"
|
305
|
+
code.indent=0
|
306
|
+
code << "end"
|
307
|
+
when Type
|
308
|
+
if is_basic_type(type) #base types
|
309
|
+
code << "#{ast_node_var}_.#{attr.name} = acceptIt"
|
310
|
+
else
|
311
|
+
code << "#{ast_node_var}_.#{attr.name} = parse#{type.name}()"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
return code
|
315
|
+
end
|
316
|
+
|
317
|
+
def is_basic_type type
|
318
|
+
type.name.to_s.upcase==type.name.to_s
|
319
|
+
end
|
320
|
+
|
321
|
+
def compute_possible_starters type
|
322
|
+
ret=[]
|
323
|
+
sons=find_sons_of(type)
|
324
|
+
starters= (sons << type).flatten
|
325
|
+
ret=starters.collect{|starter| starter.name.to_s.downcase.to_sym}
|
326
|
+
ret=ret.collect{|sym| (BASIC_TYPES.include? sym) ? (sym.to_s+"_lit").to_sym : sym}
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|