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.
@@ -0,0 +1,79 @@
1
+ require_relative 'indent'
2
+ require_relative 'code'
3
+
4
+ module <%=mm.name%>
5
+
6
+ class DotPrinter
7
+
8
+ include Indent
9
+
10
+ attr_accessor :code,:nodes_decl,:nodes_cnx
11
+
12
+ def print ast #entry method
13
+ @verbose=false
14
+ @nodes_decl=Code.new
15
+ @nodes_cnx=Code.new
16
+ @printed_cnx={} #Cosmetic ! to keep track of already printed cnx source->sink
17
+ @code=Code.new
18
+ code << "digraph G {"
19
+ code.indent=2
20
+ code << "ordering=out;"
21
+ code << "ranksep=.4;"
22
+ code << "bgcolor=\"lightgrey\";"
23
+ code.newline
24
+ code << "node [shape=box, fixedsize=false, fontsize=12, fontname=\"Helvetica-bold\", fontcolor=\"blue\""
25
+ code << " width=.25, height=.25, color=\"black\", fillcolor=\"white\", style=\"filled, solid, bold\"];"
26
+ code << "edge [arrowsize=.5, color=\"black\", style=\"bold\"]"
27
+ process(ast)
28
+ code << @nodes_decl
29
+ code << @nodes_cnx
30
+ code.indent=0
31
+ code << "}"
32
+ return code
33
+ end
34
+
35
+ def process node,level=0
36
+ id=node.object_id
37
+ if node.is_a? Token
38
+ sink="#{id}"
39
+ val=node.val
40
+ nodes_decl << "#{sink} [label=\"#{val}\",color=\"red\"]"
41
+ else
42
+
43
+ kname=node.class.name.split("::")[1]
44
+ id=node.object_id
45
+ nodes_decl << "#{id} [label=\"#{kname}\"]"
46
+
47
+ node.instance_variables.each{|vname|
48
+ ivar=node.instance_variable_get(vname)
49
+ vname=vname.to_s[1..-1]
50
+ case ivar
51
+ when Array
52
+ ivar.each_with_index{|e,idx|
53
+ sink=process(e,level+2)
54
+ @printed_cnx[id]||=[]
55
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}[#{idx}]\"]" if not @printed_cnx[id].include? sink
56
+ @printed_cnx[id] << sink
57
+ }
58
+ when Token
59
+ val=ivar.val
60
+ sink="#{ivar.object_id}"
61
+ nodes_decl << "#{sink} [label=\"#{val}\",color=\"red\"]"
62
+ @printed_cnx[id]||=[]
63
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
64
+ @printed_cnx[id] << sink
65
+ when nil
66
+ else
67
+ sink=process(ivar,level+2)
68
+ @printed_cnx[id]||=[]
69
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
70
+ @printed_cnx[id] << sink
71
+ end
72
+
73
+ }
74
+ end
75
+ return id
76
+ end
77
+
78
+ end #class
79
+ end #module
@@ -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
@@ -0,0 +1,59 @@
1
+ require_relative '<%=mm.name.to_s.downcase%>_parser'
2
+ require_relative '<%=mm.name.to_s.downcase%>_pp'
3
+ require_relative '<%=mm.name.to_s.downcase%>_ast_printer'
4
+ require_relative '<%=mm.name.to_s.downcase%>_visitor'
5
+
6
+ module <%=mm.name%>
7
+
8
+ class Compiler
9
+ attr_accessor :ast
10
+
11
+ def initialize
12
+ puts "<%=mm.name%> compiler"
13
+ end
14
+
15
+ def compile filename
16
+ @ast=parse(filename)
17
+ visit
18
+ pretty_print
19
+ dot_print
20
+ end
21
+
22
+ def parse filename
23
+ @filename=filename
24
+ name,@suffix=filename.split("/").last.split(".")
25
+ @basename=File.basename(filename,"."+@suffix)
26
+ puts "==> parsing #{filename}"
27
+ <%=mm.name%>::Parser.new.parse(filename)
28
+ end
29
+
30
+ def visit
31
+ puts "==> dummy visit"
32
+ <%=mm.name%>::Visitor.new.visit(ast)
33
+ end
34
+
35
+ def pretty_print
36
+ target="#{@basename}_pp.#{@suffix}"
37
+ puts "==> pretty print to ............ #{target}"
38
+ code_str=<%=mm.name%>::PrettyPrinter.new.print(ast)
39
+ File.open("#{target}",'w'){|f| f.puts code_str}
40
+ end
41
+
42
+ def dot_print
43
+ target="#{@basename}.dot"
44
+ puts "==> generate dot for AST in .... #{target}"
45
+ code=<%=mm.name%>::DotPrinter.new.print(ast)
46
+ code.save_as(target,verbose=false)
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ if $PROGRAM_NAME == __FILE__
53
+ filename=ARGV[0]
54
+ raise "need a <%=mm.name%> file !" if filename.nil?
55
+ t1 = Time.now
56
+ <%=mm.name%>::Compiler.new.compile(filename)
57
+ t2 = Time.now
58
+ puts "compiled in : #{t2-t1} s"
59
+ end
@@ -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
@@ -0,0 +1,139 @@
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
+ when Class
21
+ return kind==Token
22
+ else
23
+ raise "wrong type for token test"
24
+ end
25
+ end
26
+
27
+ def accept dummy,args=nil
28
+ val
29
+ end
30
+ end
31
+
32
+ module <%=mm.name%>
33
+
34
+ class Lexer
35
+ #................................................
36
+ NEWLINE = /[\n]/
37
+ SPACE = /[ \t]+/
38
+ #.............punctuation........................
39
+ LPAREN = /\A\(/
40
+ RPAREN = /\A\)/
41
+ COMMENT = /\A\#(.*)/
42
+ # .............M3E specific literals.............
43
+ IDENT = /\A[a-zA-Z]+[0-9a-zA-Z_]*/i
44
+ FLOAT = /\A[0-9]+\.[0-9]+/
45
+ FRAC = /\A[0-9]+\/[0-9]+/
46
+ INTEGER = /\A[0-9]+/
47
+ RANGE = /\A#{INTEGER}\.\.#{INTEGER}/
48
+ STRING = /\A\"(.*)\"/ # need to check!
49
+ NIL = /\Anil/
50
+ TRUE = /\A\true/
51
+ FALSE = /\Afalse/
52
+ #..............DSL keywords......................
53
+ <%=keywords_regexp%>
54
+
55
+ def initialize str=''
56
+ init(str)
57
+ end
58
+
59
+ def init str
60
+ @ss=StringScanner.new(str)
61
+ @line=0
62
+ end
63
+
64
+ def tokenize str
65
+ @tokens=[]
66
+ init(str)
67
+ until @ss.eos?
68
+ @tokens << next_token()
69
+ end
70
+ return @tokens[0..-2]
71
+ end
72
+
73
+ #next token can detect spaces length
74
+ def next_token
75
+
76
+ if @ss.bol?
77
+ @line+=1
78
+ @old_pos=@ss.pos
79
+ end
80
+
81
+ position=[@line,@ss.pos-@old_pos+1]
82
+
83
+ return :eos if @ss.eos?
84
+
85
+ case
86
+ when text = @ss.scan(NEWLINE)
87
+ next_token()
88
+ when text = @ss.scan(SPACE)
89
+ next_token()
90
+ when text = @ss.scan(LPAREN)
91
+ return Token.new [:lparen,text,position]
92
+ when text = @ss.scan(COMMENT)
93
+ return Token.new [:m3e_comment,text,position]
94
+ when text = @ss.scan(RPAREN)
95
+ return Token.new [:rparen,text,position]
96
+ when text = @ss.scan(FLOAT)
97
+ return Token.new [:float_lit,text,position]
98
+ when text = @ss.scan(FRAC)
99
+ return Token.new [:frac_lit,text,position]
100
+ when text = @ss.scan(RANGE)
101
+ return Token.new [:range_lit,text,position]
102
+ when text = @ss.scan(INTEGER)
103
+ return Token.new [:integer_lit,text,position]
104
+ when text = @ss.scan(STRING)
105
+ return Token.new [:string_lit,text,position]
106
+ when text = @ss.scan(FRAC)
107
+ return Token.new [:frac_lit,text,position]
108
+ when text = @ss.scan(IDENT)
109
+ case
110
+ <%=apply_regexp%>
111
+ when value = text.match(NIL)
112
+ return Token.new [:nil,text,position]
113
+ when value = text.match(TRUE)
114
+ return Token.new [:true,text,position]
115
+ when value = text.match(FALSE)
116
+ return Token.new [:false,text,position]
117
+ else
118
+ return Token.new [:identifier,text,position]
119
+ end
120
+ else
121
+ x = @ss.getch
122
+ return Token.new [x, x,position]
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ if $PROGRAM_NAME == __FILE__
129
+ str=IO.read(ARGV[0])
130
+ #str.downcase!
131
+ puts str
132
+ t1 = Time.now
133
+ lexer=<%=mm.name%>::Lexer.new
134
+ tokens=lexer.tokenize(str)
135
+ t2 = Time.now
136
+ pp tokens
137
+ puts "number of tokens : #{tokens.size}"
138
+ puts "tokenized in : #{t2-t1} s"
139
+ end
@@ -0,0 +1,75 @@
1
+ require 'pp'
2
+ require_relative 'indent'
3
+ require_relative '<%=mm.name.to_s.downcase%>_lexer'
4
+ require_relative '<%=mm.name.to_s.downcase%>_ast'
5
+
6
+ module <%=mm.name%>
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
+ last_pos = @tokens.last.pos
22
+ @tokens << Token.new([:eos,'',last_pos])
23
+ pp @tokens if @verbose
24
+ parse<%=mm.classes.first.name%>
25
+ end
26
+
27
+ def acceptIt
28
+ tok=tokens.shift
29
+ say "consuming #{tok.val} (#{tok.kind})"
30
+ tok
31
+ end
32
+
33
+ def showNext n=0
34
+ tokens[n]
35
+ end
36
+
37
+ def expect kind
38
+ if (actual=showNext.kind)!=kind
39
+ abort "ERROR at #{showNext.pos}. Expecting #{kind}. Got #{actual}"
40
+ else
41
+ return acceptIt()
42
+ end
43
+ end
44
+
45
+ def nil_maybe?
46
+ if showNext.is_a?(:nil)
47
+ return acceptIt
48
+ else
49
+ return nil
50
+ end
51
+ end
52
+
53
+ def parse_comments
54
+ ret=nil
55
+ while showNext.is_a? :m3e_comment
56
+ str||=""
57
+ str << acceptIt.val
58
+ end
59
+ if str
60
+ ret=Token.new([:comment,str,[0,0]])
61
+ end
62
+ ret
63
+ end
64
+ <%=parsing_methods%>
65
+
66
+ end
67
+ end
68
+ if $PROGRAM_NAME == __FILE__
69
+ filename=ARGV[0]
70
+ raise "need a file !" if filename.nil?
71
+ t1 = Time.now
72
+ <%=mm.name%>::Parser.new.parse(filename)
73
+ t2 = Time.now
74
+ puts "parsed in : #{t2-t1} s"
75
+ end
@@ -0,0 +1,46 @@
1
+ require 'pp'
2
+ require_relative '<%=mm.name.to_s.downcase%>_ast'
3
+ require_relative 'indent'
4
+ require_relative 'code'
5
+
6
+ module <%=mm.name%>
7
+
8
+ class PrettyPrinter
9
+
10
+ include Indent
11
+
12
+ def print ast
13
+ ary=ast.accept(self)
14
+ str=prpr(ary)
15
+ end
16
+
17
+ def prpr ary
18
+ print_rec(ary).lstrip
19
+ end
20
+
21
+ def print_rec ary,indent=0
22
+ str=""
23
+ case e=ary
24
+ when Array
25
+ str << "\n"
26
+ if e.first.to_s.start_with? "#"
27
+ str << " "*indent+ary.shift+"\n"
28
+ end
29
+ str << " "*indent+"("
30
+ ary.each do |e|
31
+ str << print_rec(e,indent+2)
32
+ end
33
+ if ary.last.is_a? Array
34
+ str << "\n"+" "*indent+")"
35
+ else
36
+ str<< "\b)"
37
+ end
38
+ else
39
+ str << e.to_s+ " "
40
+ end
41
+ return str
42
+ end
43
+
44
+ <%=visiting_methods%>
45
+ end #class
46
+ end #module
@@ -0,0 +1,18 @@
1
+ require_relative '<%=mm.name.to_s.downcase%>_ast'
2
+ require_relative 'indent'
3
+ require_relative 'code'
4
+
5
+ module <%=mm.name%>
6
+
7
+ class Visitor
8
+
9
+ include Indent
10
+
11
+ def visit ast
12
+ @verbose=true
13
+ ast.accept(self)
14
+ end
15
+
16
+ <%=visiting_methods%>
17
+ end #class
18
+ end #module
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Astrapi
2
+ VERSION="0.0.5"
3
+ end
data/lib/visitor.rb ADDED
@@ -0,0 +1,53 @@
1
+ require_relative 'ast'
2
+ require_relative 'indent'
3
+ require_relative 'code'
4
+
5
+ module Astrapi
6
+
7
+ class Visitor
8
+
9
+ include Indent
10
+
11
+ def visit ast
12
+ ast.accept(self)
13
+ end
14
+
15
+ def visitModule modul,args=nil
16
+ indent "visitModule"
17
+ name=modul.name.accept(self)
18
+ modul.classes.each{|k| k.accept(self)}
19
+ dedent
20
+ end
21
+
22
+ def visitKlass klass,args=nil
23
+ indent "visitKlass #{klass.name.sym}"
24
+ klass.inheritance.accept(self)
25
+ klass.attrs.each{|attr| attr.accept(self)}
26
+ dedent
27
+ end
28
+
29
+ def visitAttr attr,args=nil
30
+ indent "visitAttr"
31
+ attr.name.accept(self)
32
+ attr.type.accept(self)
33
+ dedent
34
+ end
35
+
36
+ def visitType type,args=nil
37
+ indent "visitType"
38
+ end
39
+
40
+ def visitArrayOf arrayOf,args=nil
41
+ indent "visitArrayOf"
42
+ arrayOf.type.accept(self)
43
+ dedent
44
+ end
45
+
46
+ def visitIdentifier id,args=nil
47
+ indent "visitIdentifier"
48
+ say " - #{id.sym}"
49
+ dedent
50
+ end
51
+
52
+ end
53
+ end
data/tests/geometry.mm ADDED
@@ -0,0 +1,32 @@
1
+ module Geometry
2
+
3
+ class Scene
4
+ attr name => IDENT
5
+ attr elements => Shape[]
6
+ end
7
+
8
+ class Shape
9
+ attr id => IDENT
10
+ attr position => Position
11
+ end
12
+
13
+ class Square < Shape
14
+ attr size => Size
15
+ end
16
+
17
+ class Circle < Shape
18
+ attr radius => Size
19
+ end
20
+
21
+ class Rectangle < Shape
22
+ attr size => Size
23
+ end
24
+
25
+ class Size
26
+ attr dims => INTEGER[]
27
+ end
28
+
29
+ class Position
30
+ attr coord => INTEGER[]
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: astrapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Jean-Christophe Le Lann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Starting from the metamodel of a DSL (abstract classes and their relationship),
14
+ Astrapi generates a compiler front end for this DSL. The model itself is expressed
15
+ in S-expressions
16
+ email: lelannje@ensta-bretagne.fr
17
+ executables:
18
+ - astrapi
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - bin/astrapi
23
+ - doc/astrapi.html
24
+ - lib/ast.rb
25
+ - lib/checker.rb
26
+ - lib/class_diagram_printer.rb
27
+ - lib/code.rb
28
+ - lib/compiler.rb
29
+ - lib/dbg.rb
30
+ - lib/dot_printer.rb
31
+ - lib/indent.rb
32
+ - lib/lexer.rb
33
+ - lib/parser.rb
34
+ - lib/pretty_printer.rb
35
+ - lib/ruby_generator.rb
36
+ - lib/template_ast_printer.rb
37
+ - lib/template_code.rb
38
+ - lib/template_compiler.rb
39
+ - lib/template_indent.rb
40
+ - lib/template_lexer.rb
41
+ - lib/template_parser.rb
42
+ - lib/template_pretty_printer.rb
43
+ - lib/template_visitor.rb
44
+ - lib/version.rb
45
+ - lib/visitor.rb
46
+ - tests/geometry.mm
47
+ homepage: http://www.jcll.fr/astrapi.html
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message: Thanks for installing! A small doc & examples are in astrapi/doc
52
+ and astrapi/tests.
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.0.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.4.6
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: meta-compiler for S-expression based Domain Specific Languages (DSL)
72
+ test_files: []