astrapi 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 +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
|