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