astrapi 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 10ee1c3dea18bdd7755a762060ccc1a1aa0692ca
4
+ data.tar.gz: cb41eab2377dafbac4d3b79c90e88873ec83973e
5
+ SHA512:
6
+ metadata.gz: d327cb9aa4133ef33a4c47b191fbea985b2adc7e746d952077b1ec5caee7820fcfc9382b980a5e4966fd1b21490cecca79beeb5f7cb436eaf0321a1af08fcd70
7
+ data.tar.gz: 342378734808d44a6dd3993d593d6036669f7e3dd6be18aed39b966798d7416a309b2a8337d8850c9590591af093f4d0dbb80985229af075cb87929811267857
data/bin/astrapi ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../lib/compiler"
3
+
4
+ t1 = Time.now
5
+ compiler=Astrapi::Compiler.new
6
+ filename=compiler.analyze_options(ARGV)
7
+ compiler.compile(filename)
8
+ t2 = Time.now
9
+ puts "compiled in : #{t2-t1} s" if $options[:verbosity]>=2
data/doc/astrapi.html ADDED
@@ -0,0 +1,149 @@
1
+ <h1 id="astrapi-meta-compiler">Astrapi meta-compiler</h1>
2
+
3
+ <h2 id="what-is-astrapi-">What is Astrapi ?</h2>
4
+
5
+ <p>Astrapi is meta-compiler for Sexp-based Domain Specific Languages : once you have described your DSL concepts (abstract syntax) thanks to Astrapi language, the compiler generates several files for you:</p>
6
+
7
+ <ul>
8
+ <li>the corresponding classes</li>
9
+ <li>lexer and parser</li>
10
+ <li>generic visitor</li>
11
+ <li>pretty printer</li>
12
+ <li>graphical AST viewer</li>
13
+ </ul>
14
+
15
+ <p>Finally a driver for you own DSL compiler is also generated. Version 0.0.2 provides Ruby generation, but Python, Java and C++ will be available soon.</p>
16
+
17
+ <p>Astrapi-generated DSL parser will assume your DSL models are written in plain <strong>s-expressions</strong>.</p>
18
+
19
+ <h2 id="what-are-s-expressions-">What are s-expressions ?</h2>
20
+ <p>S-expressions, abreviated as "sexps", actually mean "symbolic expressions". They originated from the famous LISP language. Compiler designers resort to <em>sexp</em> as the most direct mean to capture Abstract Syntax Trees (AST) in a textual format.</p>
21
+
22
+ <p>Sexps are convenient to serialize both data <em>and</em> code, which offers a superiority over other serialization formats like XML, YAML or JSON.</p>
23
+
24
+ <p>It may be noticed that several S-expressions parsers exist around. In the Ruby ecosystems, SXP and Sexpistols can be recommanded. However, these parsers will just turn the parenthesized expressions into a Ruby native data structure : namely arrays of arrays, etc. If your intent is to consider each s-expression as an instance of a <em>custom</em> class, then Astrapi is for you !</p>
25
+
26
+ <h2 id="how-to-install-">How to install ?</h2>
27
+ <p>In your terminal, simply type : <strong>gem install astrapi</strong></p>
28
+
29
+ <h2 id="quick-start">Quick start</h2>
30
+ <p>In this example, we invent a toy language (DSL) that aims at describing simple geometry. Let us begin with examples programs written in our expected syntax :</p>
31
+
32
+ <!-- HTML generated using hilite.me -->
33
+ <div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">(<span style="color: #0066BB; font-weight: bold">scene</span> <span style="color: #996633">test</span>
34
+ (<span style="color: #0066BB; font-weight: bold">square</span> <span style="color: #996633">s1</span>
35
+ (<span style="color: #0066BB; font-weight: bold">position</span> <span style="color: #0000DD; font-weight: bold">123</span> <span style="color: #0000DD; font-weight: bold">345</span>)
36
+ (<span style="color: #0066BB; font-weight: bold">size</span> <span style="color: #0000DD; font-weight: bold">12</span>)
37
+ )
38
+ (<span style="color: #0066BB; font-weight: bold">circle</span> <span style="color: #996633">c1</span>
39
+ (<span style="color: #0066BB; font-weight: bold">position</span> <span style="color: #0000DD; font-weight: bold">123</span> <span style="color: #0000DD; font-weight: bold">345</span>)
40
+ (<span style="color: #0066BB; font-weight: bold">size</span> <span style="color: #0000DD; font-weight: bold">12</span> <span style="color: #0000DD; font-weight: bold">23</span>)
41
+ )
42
+ (<span style="color: #0066BB; font-weight: bold">rectangle</span> <span style="color: #996633">s2</span>
43
+ (<span style="color: #0066BB; font-weight: bold">position</span> <span style="color: #0000DD; font-weight: bold">323</span> <span style="color: #0000DD; font-weight: bold">445</span>)
44
+ (<span style="color: #0066BB; font-weight: bold">size</span> <span style="color: #0000DD; font-weight: bold">12</span> <span style="color: #0000DD; font-weight: bold">34</span>)
45
+ )
46
+ )
47
+ </pre></div>
48
+
49
+ <p>Now let's express the concepts of this model : let's name this a <em>metamodel</em>. I suffix this file with '.mm'. It ressembles <em>Ruby modules and class</em>, but it is not.</p>
50
+
51
+ <!-- HTML generated using hilite.me -->
52
+ <div style="background: #f0f3f3; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #006699; font-weight: bold">module</span> <span style="color: #00CCFF; font-weight: bold">Geometry</span>
53
+
54
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Scene</span>
55
+ <span style="color: #006699">attr</span> elements <span style="color: #555555">=&gt;</span> <span style="color: #336600">Shape</span><span style="color: #555555">[]</span>
56
+ <span style="color: #006699; font-weight: bold">end</span>
57
+
58
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Shape</span>
59
+ <span style="color: #006699">attr</span> <span style="color: #336666">id</span> <span style="color: #555555">=&gt;</span> <span style="color: #336600">IDENT</span>
60
+ <span style="color: #006699">attr</span> position <span style="color: #555555">=&gt;</span> <span style="color: #336600">Position</span>
61
+ <span style="color: #006699; font-weight: bold">end</span>
62
+
63
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Square</span> <span style="color: #555555">&lt;</span> <span style="color: #336600">Shape</span>
64
+ <span style="color: #006699">attr</span> size <span style="color: #555555">=&gt;</span> <span style="color: #336600">INTEGER</span>
65
+ <span style="color: #006699; font-weight: bold">end</span>
66
+
67
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Circle</span> <span style="color: #555555">&lt;</span> <span style="color: #336600">Shape</span>
68
+ <span style="color: #006699">attr</span> radius <span style="color: #555555">=&gt;</span> <span style="color: #336600">INTEGER</span>
69
+ <span style="color: #006699; font-weight: bold">end</span>
70
+
71
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Rectangle</span> <span style="color: #555555">&lt;</span> <span style="color: #336600">Shape</span>
72
+ <span style="color: #006699">attr</span> size <span style="color: #555555">=&gt;</span> <span style="color: #336600">Size</span>
73
+ <span style="color: #006699; font-weight: bold">end</span>
74
+
75
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Size</span>
76
+ <span style="color: #006699">attr</span> dims <span style="color: #555555">=&gt;</span> <span style="color: #336600">INTEGER</span><span style="color: #555555">[]</span>
77
+ <span style="color: #006699; font-weight: bold">end</span>
78
+
79
+ <span style="color: #006699; font-weight: bold">class</span> <span style="color: #00AA88; font-weight: bold">Position</span>
80
+ <span style="color: #006699">attr</span> coord <span style="color: #555555">=&gt;</span> <span style="color: #336600">INTEGER</span><span style="color: #555555">[]</span>
81
+ <span style="color: #006699; font-weight: bold">end</span>
82
+ <span style="color: #006699; font-weight: bold">end</span>
83
+ </pre></div>
84
+
85
+ <p>Then compile this metamodel using Astrapi :</p>
86
+
87
+ <!-- HTML generated using hilite.me -->
88
+ <div style="background: #f0f3f3; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">
89
+ jcll$ &gt; astrapi geometry.mm
90
+ ASTRAPI meta-compiler for Sexp-based DSLs (c) J-C Le Lann 2016
91
+ ==&gt; parsing metamodel.................... geometry.mm
92
+ ==&gt; pretty print metamodel............... geometry_pp.mm
93
+ ==&gt; generate dot for metamodel........... geometry_ast.dot
94
+ ==&gt; checking metamodel
95
+ ==&gt; generating class diagram............. geometry_class_diagram.dot
96
+ ==&gt; generate software stack for DSL &#39;Geometry&#39;. Ruby version
97
+ ----&gt; generating Geometry DSL AST classes
98
+ ----&gt; generating Geometry DSL AST printer
99
+ ----&gt; generating Geometry DSL lexer
100
+ ----&gt; generating Geometry DSL parser
101
+ ----&gt; generating Geometry DSL pretty printer
102
+ ----&gt; generating Geometry DSL compiler
103
+ </pre></div>
104
+
105
+ <p>Now we can play with our brand new Geometry compiler !</p>
106
+
107
+ <!-- HTML generated using hilite.me -->
108
+ <div style="background: #f8f8f8; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">$&gt; ruby geometry_compiler.rb ex1.geo
109
+ Geometry compiler
110
+ ==&gt; parsing ex1.geo
111
+ ==&gt; pretty print to ........ ex1.geo_pp.geometry
112
+ ==&gt; generate dot for AST in ex1.geo.dot
113
+ compiled in : 0.001463371 s
114
+ </pre></div>
115
+
116
+ <p>In particular, we can have a look at the graphical AST (plain graphiz dot format). If you have xdot installed, simply type <strong>xdot ex1.geo.dot</strong> and the AST of our example will show up like this.</p>
117
+
118
+ <p><img src="./img/ex1.geo.png" alt="Image of AST model" /></p>
119
+
120
+ <h2 id="about-the-metamodel">About the Metamodel</h2>
121
+
122
+ <h3 id="basic-syntax">Basic syntax</h3>
123
+ <p>Astrapi metamodel syntax remains very basic. A concept is captured as a <strong>class</strong>, followed by the list of its attributes <strong>attr</strong>. The type of the attributes appears after <strong>=&gt;</strong>. Attributes can be simple or multiple. The order of declaration of classes has no importance, but the <strong>order of <em>attr</em> declarations is fundamental</strong>, as it drives the generated DSL parser. This may be enhanced in a future version.</p>
124
+
125
+ <h3 id="basic-data-types">Basic data types</h3>
126
+ <p>There also exist some basic types understood by Astrapi and parsed as <em>terminals</em> :</p>
127
+
128
+ <ul>
129
+ <li><strong>IDENT</strong> will make the lexer recognize tokens matching the regexp /[a-zA-Z]+[a-zA-Z_0-9]*/i.</li>
130
+ <li><strong>INTEGER</strong> : regexp is /[0-9]+/</li>
131
+ <li><strong>FLOAT</strong> : regexp is /[0-9]+.[0-9]+/</li>
132
+ <li><strong>RANGE</strong> : regexp is /[0-9]+..[0-9+/</li>
133
+ <li><strong>STRING</strong> : regexp is /"(.*)"/</li>
134
+ <li><strong>NIL</strong> : regexp is /nil/</li>
135
+ </ul>
136
+
137
+ <p>Note that you can use <strong>nil</strong> in your DSL model, either for expected concepts or terminals.</p>
138
+
139
+ <h3 id="graphical-view-of-the-metamodel">Graphical view of the Metamodel</h3>
140
+ <p>A graphical view of the metamodel structure is also generated by Astrapi. This is a basic <em>class-diagram</em> of the AST classes. The name of the dot file appears during the metamodel compilation. It is suffix a ** xxx_ast.dot <strong>.
141
+ To view it, type **xdot xxx_ast.dot</strong>. For our Geometry example :</p>
142
+
143
+ <p><img src="./img/geometry_class_diagram.png" alt="Image of the metamodel" /></p>
144
+
145
+ <h2 id="comments-in-astrapi-s-expressions">Comments in Astrapi s-expressions</h2>
146
+ <p>Comments are accepted in Astrapi s-expressions. Comments starts with <strong>#</strong> symbol and ends at the end of line. A comment will be attached (attribute <strong>.comments_</strong>) to the AST node following this comment. As such, comments can be reused (code generation etc).</p>
147
+
148
+ <h2 id="credits">Credits</h2>
149
+ <p>Please <a href="mailto:lelannje@ensta-bretagne.fr">drop me an email</a> if you use Astrapi, or want some additional features or bug fixes.</p>
data/lib/ast.rb ADDED
@@ -0,0 +1,68 @@
1
+ module Astrapi
2
+
3
+ class Ast
4
+ def accept(visitor, arg=nil)
5
+ name = self.class.name.split(/::/)[1]
6
+ visitor.send("visit#{name}".to_sym, self ,arg) # Metaprograming !
7
+ end
8
+ end
9
+
10
+ class Module < Ast
11
+ attr_accessor :name,:classes
12
+ def initialize name,classes=[]
13
+ @name,@classes=name,classes
14
+ end
15
+ end
16
+
17
+ class Klass < Ast
18
+ attr_accessor :name,:inheritance,:attrs
19
+ def initialize name,inheritance=nil,attrs=[]
20
+ @name,@inheritance,@attrs=name,inheritance,attrs
21
+ end
22
+ end
23
+
24
+ class Attr < Ast
25
+ attr_accessor :name,:type
26
+ def initialize name,type
27
+ @name,@type=name,type
28
+ end
29
+ end
30
+
31
+ class Type < Ast
32
+ attr_accessor :name
33
+ def initialize name
34
+ @name=name
35
+ end
36
+
37
+ def ==(other)
38
+ name==other.name
39
+ end
40
+ end
41
+
42
+ class ArrayOf < Type
43
+ attr_accessor :type
44
+ def initialize type
45
+ @type=type
46
+ end
47
+ end
48
+
49
+ #..............................
50
+ class Identifier < Ast
51
+ def initialize tok
52
+ @tok=tok
53
+ end
54
+
55
+ def str
56
+ @tok.val.to_s
57
+ end
58
+
59
+ def to_s
60
+ str
61
+ end
62
+
63
+ def ==(other)
64
+ str==other.str
65
+ end
66
+ end
67
+
68
+ end
data/lib/checker.rb ADDED
@@ -0,0 +1,61 @@
1
+ require_relative 'ast'
2
+ require_relative 'visitor'
3
+ require_relative 'code'
4
+
5
+ module Astrapi
6
+
7
+ class Checker < Visitor
8
+
9
+ # the checker checks :
10
+ # - that classes are uniquely defined
11
+ # - that attrs are uniquely defined withing a given class
12
+ # - that attrs types are defined in the metamodel
13
+ attr_accessor :mm
14
+
15
+ def check ast
16
+ @verbose=false
17
+ @mm=ast
18
+ ast.accept(self)
19
+ end
20
+
21
+ def visitModule modul,args=nil
22
+ indent "visitModule"
23
+ name=modul.name.accept(self)
24
+ modul.classes.each{|k| k.accept(self)}
25
+ dedent
26
+ end
27
+
28
+ def visitKlass klass,args=nil
29
+ indent "visitKlass #{klass.name}"
30
+ if klass.inheritance
31
+
32
+ end
33
+ klass.attrs.each{|attr| attr.accept(self)}
34
+ dedent
35
+ end
36
+
37
+ def visitAttr attr,args=nil
38
+ indent "visitAttr"
39
+ attr.name.accept(self)
40
+ attr.type.accept(self)
41
+ dedent
42
+ end
43
+
44
+ def visitType type,args=nil
45
+ indent "visitType"
46
+ dedent
47
+ end
48
+
49
+ def visitArrayOf arrayOf,args=nil
50
+ indent "visitArrayOf"
51
+ arrayOf.type.accept(self)
52
+ dedent
53
+ end
54
+
55
+ def visitIdentifier id,args=nil
56
+ indent "visitIdentifier"
57
+ say " - #{id}"
58
+ dedent
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,83 @@
1
+ require_relative 'ast'
2
+ require_relative 'indent'
3
+ require_relative 'code'
4
+
5
+ module Astrapi
6
+
7
+ class ClassDiagramPrinter
8
+
9
+ include Indent
10
+
11
+ DECL_FOR_INSTRINSICS={
12
+ :IDENT => "IDENT[label=\"{IDENT|+kind\n+val\n+pos\n\}\"]",
13
+ :FLOAT => "FLOAT[label=\"{FLOAT|+kind\n+val\n+pos\n\}\"]",
14
+ :INTEGER => "INTEGER[label=\"{INTEGER|+kind\n+val\n+pos\n\}\"]",
15
+ :STRING => "STRING[label=\"{STRING|+kind\n+val\n+pos\n\}\"]",
16
+ :FRAC => "FRAC[label=\"{FRAC|+kind\n+val\n+pos\n\}\"]",
17
+ :NIL => "NIL[label=\"{NIL|+kind\n+val\n+pos\n\}\"]"
18
+ }
19
+
20
+ def print ast
21
+ ast.accept(self)
22
+ end
23
+
24
+ def visitModule modul,args=nil
25
+ indent "visitModule"
26
+
27
+ decl=Code.new # declarations of nodes
28
+ modul.classes.collect do |k|
29
+ attrs_str=k.attrs.collect{|attr| "+ #{attr.name}\\n"}
30
+ decl << "#{k.name}[label = \"{#{k.name}|#{attrs_str.join()}|...}\"]"
31
+ end
32
+ cnx=Code.new #connexions
33
+ modul.classes.each{|k| cnx << k.accept(self)}
34
+ #................................................................
35
+ code=Code.new
36
+ code << "digraph hierarchy {"
37
+ code << " size=\"5,5\""
38
+ #code << " splines=ortho"
39
+ code << " node[shape=record,style=filled,fillcolor=gray95]"
40
+ code << " edge[dir=back, arrowtail=empty]"
41
+ code.newline
42
+ code.indent=2
43
+ code << decl
44
+ code << cnx
45
+ code.indent=0
46
+ code << "}"
47
+ dedent
48
+ return code
49
+ end
50
+
51
+ def visitKlass klass,args=nil
52
+ indent "visitKlass"
53
+ code = Code.new
54
+ klass.attrs.each do |attr|
55
+ case type=attr.type
56
+ when ArrayOf
57
+ sink=type.type.name.to_s
58
+ head="label=\"*\""
59
+ when Type
60
+ sink=type.name.to_s
61
+ head="label=1"
62
+ code << DECL_FOR_INSTRINSICS[sink.to_sym] if is_instrinsic(type)
63
+ end
64
+ code << "#{klass.name} -> #{sink}[#{head},arrowtail=diamond]"
65
+ end
66
+ if klass.inheritance
67
+ code << "#{klass.inheritance} -> #{klass.name}"
68
+ end
69
+ dedent
70
+ code
71
+ end
72
+
73
+ def is_instrinsic type
74
+ if type.respond_to? :type
75
+ str=type.type.name.to_s
76
+ return str==str.upcase
77
+ else
78
+ str=type.name.to_s
79
+ return str==str.upcase
80
+ end
81
+ end
82
+ end
83
+ end
data/lib/code.rb ADDED
@@ -0,0 +1,48 @@
1
+ class Code
2
+
3
+ attr_accessor :indent,:code
4
+
5
+ def initialize indent=0
6
+ @code=[]
7
+ @indent=indent
8
+ end
9
+
10
+ def <<(str)
11
+ if str.is_a? Code
12
+ str.code.each do |line|
13
+ @code << " "*@indent+line
14
+ end
15
+ elsif str.is_a? Array
16
+ str.each do |kode|
17
+ @code << kode
18
+ end
19
+ elsif str.nil?
20
+ else
21
+ @code << " "*@indent+str
22
+ end
23
+ end
24
+
25
+ def finalize dot=false
26
+ if dot
27
+ return @code.join('\n')
28
+ end
29
+ @code.join("\n") if @code.any?
30
+ end
31
+
32
+ def newline
33
+ @code << " "
34
+ end
35
+
36
+ def save_as filename,verbose=true,sep="\n"
37
+ str=self.finalize
38
+ File.open(filename,'w'){|f| f.puts(str)}
39
+ puts "saved code in file #{filename}" if verbose
40
+ return filename
41
+ end
42
+
43
+ def size
44
+ @code.size
45
+ end
46
+
47
+
48
+ end
data/lib/compiler.rb ADDED
@@ -0,0 +1,149 @@
1
+ require 'pp'
2
+ require 'optparse'
3
+
4
+ require_relative 'dbg'
5
+ require_relative 'version'
6
+ require_relative 'parser'
7
+ require_relative 'checker'
8
+ require_relative 'pretty_printer'
9
+ require_relative 'dot_printer'
10
+ require_relative 'class_diagram_printer'
11
+ require_relative 'ruby_generator'
12
+
13
+ module Astrapi
14
+
15
+ $options={
16
+ :verbosity => 0
17
+ }
18
+
19
+ class Compiler
20
+
21
+ include Dbg
22
+
23
+ attr_accessor :mm
24
+
25
+ def initialize
26
+ puts "ASTRAPI meta-compiler for Sexp-based DSLs [version #{VERSION}] (c) J-C Le Lann 2016"
27
+ end
28
+
29
+ def analyze_options args
30
+ args << "-h" if args.empty?
31
+ opt_parser = OptionParser.new do |opts|
32
+ opts.banner = "Usage: astrapi <file.mm> [options]"
33
+
34
+ opts.on("-p", "--parse", "[P]arsing only. AST generated internally") do |n|
35
+ $options[:parse_only]=true
36
+ $options[:verbosity]=2
37
+ end
38
+
39
+ opts.on("-c", "--check", "[C]hecking only. Semantic/contextual analysis ") do |n|
40
+ $options[:check_only]=true
41
+ end
42
+
43
+ opts.on("--pp", "Pretty-print back the Astrapi code") do |n|
44
+ $options[:pp_only]=true
45
+ end
46
+
47
+ opts.on("--version", "Prints version") do |n|
48
+ puts VERSION
49
+ abort
50
+ end
51
+
52
+ targets=[:ruby,:java,:python,:cpp]
53
+ opts.on("--lang TARGET", "Target LANGUAGE (ruby by default,java,python,cpp)",targets) do |lang|
54
+ case lang
55
+ when :ruby
56
+ $options[:target_language]=lang
57
+ when :java,:python,:cpp
58
+ raise "#{lang} version not implemented yet. Sorry"
59
+ $options[:target_language]=lang
60
+ else
61
+ raise "illegal target language #{lang}"
62
+ end
63
+ end
64
+
65
+ opts.on("-v LEVEL", "--verbose level", "allow verbose levels (0,1)") do |level|
66
+ $options[:verbosity]=level.to_i
67
+ end
68
+
69
+ opts.on("-h", "--help", "Prints this help") do
70
+ puts opts
71
+ exit
72
+ end
73
+ end
74
+
75
+ begin
76
+ opt_parser.parse!(args)
77
+ rescue Exception => e
78
+ puts e
79
+ dbg 0,e.backtrace if $options[:verbosity]>2
80
+ exit
81
+ end
82
+ # remaining ARGV (remind that parse! has removed everything else)
83
+ filename = ARGV.pop
84
+ unless filename
85
+ dbg 0,"Need to specify a filename to process"
86
+ exit
87
+ end
88
+ return filename
89
+ end
90
+
91
+ def compile mm_filename
92
+ @mm=parse(mm_filename)
93
+ pretty_print
94
+ dot_print
95
+ check
96
+ generate_class_diagram
97
+ generate_ruby
98
+ end
99
+
100
+ def parse mm_filename
101
+ puts "==> parsing metamodel.................... #{mm_filename}"
102
+ ast=Astrapi::Parser.new.parse(mm_filename)
103
+ exit if $options[:parse_only]
104
+ ast
105
+ end
106
+
107
+ def pretty_print
108
+ target="#{mm.name.to_s.downcase}_pp.mm"
109
+ puts "==> pretty print metamodel............... #{target}"
110
+ code=PrettyPrinter.new.print(mm)
111
+ code.save_as(target,verbose=false)
112
+ exit if $options[:pp_only]
113
+ end
114
+
115
+ def check
116
+ puts "==> checking metamodel"
117
+ Checker.new.check(mm)
118
+ end
119
+
120
+ def generate_class_diagram
121
+ target="#{mm.name.to_s.downcase}_class_diagram.dot"
122
+ puts "==> generating class diagram............. #{target}"
123
+ code=ClassDiagramPrinter.new.print(mm)
124
+ code.save_as(target,verbose=false)
125
+ end
126
+
127
+ def dot_print
128
+ target="#{mm.name.to_s.downcase}_ast.dot"
129
+ puts "==> generate dot for metamodel........... #{target}"
130
+ code=DotPrinter.new.print(mm)
131
+ code.save_as(target,verbose=false)
132
+ end
133
+
134
+ def generate_ruby
135
+ puts "==> generate software stack for DSL '#{@mm.name}'. Ruby version"
136
+ RubyGenerator.new.generate(mm)
137
+ end
138
+
139
+ end
140
+ end
141
+
142
+ if $PROGRAM_NAME == __FILE__
143
+ filename=ARGV[0]
144
+ raise "need a file !" if filename.nil?
145
+ t1 = Time.now
146
+ Astrapi::Compiler.new.compile(filename)
147
+ t2 = Time.now
148
+ puts "compiled in : #{t2-t1} s"
149
+ end
data/lib/dbg.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Astrapi
2
+ module Dbg
3
+ def dbg level,str
4
+ puts str if $options[:verbosity]>=level
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'ast'
2
+ require_relative 'indent'
3
+ require_relative 'code'
4
+
5
+ module Astrapi
6
+
7
+ class DotPrinter
8
+
9
+ include Indent
10
+
11
+ attr_accessor :code,:nodes_decl,:nodes_cnx
12
+
13
+ def print ast #entry method
14
+ @verbose=false
15
+ @nodes_decl=Code.new
16
+ @nodes_cnx=Code.new
17
+ @printed_cnx={} #Cosmetic ! to keep track of already printed cnx source->sink
18
+ @code=Code.new
19
+ code << "digraph G {"
20
+ code.indent=2
21
+ code << "ordering=out;"
22
+ code << "ranksep=.4;"
23
+ code << "bgcolor=\"lightgrey\";"
24
+ code.newline
25
+ code << "node [shape=box, fixedsize=false, fontsize=12, fontname=\"Helvetica-bold\", fontcolor=\"blue\""
26
+ code << " width=.25, height=.25, color=\"black\", fillcolor=\"white\", style=\"filled, solid, bold\"];"
27
+ code << "edge [arrowsize=.5, color=\"black\", style=\"bold\"]"
28
+ process(ast)
29
+ code << @nodes_decl
30
+ code << @nodes_cnx
31
+ code.indent=0
32
+ code << "}"
33
+ return code
34
+ end
35
+
36
+ def process node,level=0
37
+
38
+ kname=node.class.name.split("::")[1]
39
+ id=node.object_id
40
+ nodes_decl << "#{id} [label=\"#{kname}\"]"
41
+
42
+ node.instance_variables.each{|vname|
43
+ ivar=node.instance_variable_get(vname)
44
+ vname=vname.to_s[1..-1]
45
+ case ivar
46
+ when Array
47
+ ivar.each_with_index{|e,idx|
48
+ sink=process(e,level+2)
49
+ @printed_cnx[id]||=[]
50
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}[#{idx}]\"]" if not @printed_cnx[id].include? sink
51
+ @printed_cnx[id] << sink
52
+ }
53
+ when Token
54
+ val=ivar.val
55
+ sink="#{ivar.object_id}"
56
+ nodes_decl << "#{sink} [label=\"#{val}\",color=\"red\"]"
57
+ @printed_cnx[id]||=[]
58
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
59
+ @printed_cnx[id] << sink
60
+ else
61
+ sink=process(ivar,level+2)
62
+ @printed_cnx[id]||=[]
63
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
64
+ @printed_cnx[id] << sink
65
+ end
66
+
67
+ }
68
+ return id
69
+ end
70
+
71
+ end #class
72
+ end #module
data/lib/indent.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Indent
2
+
3
+ INDENT=2
4
+
5
+ def indent str
6
+ @indentation||=-INDENT
7
+ @indentation+=INDENT
8
+ say(str)
9
+ end
10
+
11
+ def dedent
12
+ @indentation-=INDENT
13
+ end
14
+
15
+ def say str
16
+ puts " "*@indentation+str if @verbose
17
+ end
18
+
19
+ end