scrag 0.0.1

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
+ SHA256:
3
+ metadata.gz: 6497b445e4a0f1279357da48c6f451ad76e7969c7c565c857d11b89405668c90
4
+ data.tar.gz: 3f8d6c011473b47341a1b6392327bf3cc92768639fac176ee563b38d73910398
5
+ SHA512:
6
+ metadata.gz: f6e779ad79c007d369bfe72b3505072ebc8e33ba49d228d1a898575721ede6598a983c81335e673188fc009f49ce64aba10cb5a76ea5f59226ef82daa6e080f0
7
+ data.tar.gz: a0b5c131b51aff35b7c03f785733e2c244073c0cfa375e3ebc030cc3e55096e210f67e56465b8b741e1c83cd26392af1edfce4614cbf6299110f9fd70c15f064
data/bin/scrag ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/scrag'
4
+ Scrag::Runner.run(*ARGV)
data/lib/scrag/code.rb ADDED
@@ -0,0 +1,47 @@
1
+ class Code
2
+
3
+ attr_accessor :indent,:lines
4
+
5
+ def initialize str=nil
6
+ @lines=[]
7
+ (@lines << str) if str
8
+ @indent=0
9
+ end
10
+
11
+ def <<(thing)
12
+ if (code=thing).is_a? Code
13
+ code.lines.each do |line|
14
+ @lines << " "*@indent+line.to_s
15
+ end
16
+ elsif thing.is_a? Array
17
+ thing.each do |kode|
18
+ @lines << kode
19
+ end
20
+ elsif thing.nil?
21
+ else
22
+ @lines << " "*@indent+thing.to_s
23
+ end
24
+ end
25
+
26
+ def finalize
27
+ return @lines.join("\n") if @lines.any?
28
+ ""
29
+ end
30
+
31
+ def newline
32
+ @lines << " "
33
+ end
34
+
35
+ def save_as filename,verbose=true,sep="\n"
36
+ str=self.finalize
37
+ File.open(filename,'w'){|f| f.puts(str)}
38
+ puts "=> code saved as : #{filename}" if verbose
39
+ return filename
40
+ end
41
+
42
+ def size
43
+ @lines.size
44
+ end
45
+
46
+
47
+ end
@@ -0,0 +1,119 @@
1
+ require 'erb'
2
+ require_relative 'code'
3
+
4
+ module Scrag
5
+
6
+ class Compiler
7
+
8
+ attr_accessor :options
9
+ attr_accessor :project_name
10
+
11
+ def initialize options={}
12
+ @options=options
13
+ end
14
+
15
+ def compile project_name
16
+ @project_name=project_name.downcase
17
+ puts "project #{project_name}".center(65,'=')
18
+ @files_for_dir={
19
+ project_name => [:gemspec],
20
+ "bin" => [:exec],
21
+ "lib" => [:top_module],
22
+ "lib/#{project_name}" => [
23
+ :ast,
24
+ :code,
25
+ :compiler,
26
+ :exec,
27
+ :generic_lexer,
28
+ :generic_parser,
29
+ :lexer,
30
+ :parser,
31
+ :pretty_printer,
32
+ :runner,
33
+ :token,
34
+ :top_module,
35
+ :transformer,
36
+ :version,
37
+ :visitor,
38
+ ]
39
+ }
40
+ generate
41
+ end
42
+
43
+ def item n=0,str
44
+ subitem=(n==0) ? "": " "
45
+ spaces=case n
46
+ when 0 then 0
47
+ when 1 then 1
48
+ else
49
+ 1+(n-1)*4
50
+ end
51
+
52
+ puts " "*spaces+"#{subitem}[+] #{str}"
53
+ end
54
+
55
+ def generate
56
+ @pwd=Dir.pwd
57
+ check_doesnt_exists(project_name)
58
+ create "#{project_name}"
59
+ create "#{project_name}/bin"
60
+ create "#{project_name}/lib"
61
+ create "#{project_name}/lib/#{project_name}"
62
+ create "#{project_name}/tests"
63
+ end
64
+
65
+ def create dir
66
+ item "creating dir '#{dir}'"
67
+ create_stuff dir
68
+ end
69
+
70
+ def check_doesnt_exists dir
71
+ if Dir.exists?(dir)
72
+ if options[:force]
73
+ puts "WARNING : project already exists !"
74
+ puts "Are you sure you want to force generation ? files will be lost !"
75
+ key=$stdin.chomp
76
+ puts key
77
+ else
78
+ puts "Scrag ERROR : project '#{project_name}' already exists. Type -v for options."
79
+ abort
80
+ end
81
+ end
82
+ end
83
+
84
+ def create_stuff dir
85
+ Dir.mkdir dir
86
+ Dir.chdir dir
87
+ rel_dir=dir.split('/')[1..-1].join('/')
88
+ files_to_create=@files_for_dir[rel_dir]
89
+ if files_to_create
90
+ files_to_create.each do |file|
91
+ create_file(file)
92
+ end
93
+ end
94
+ Dir.chdir @pwd
95
+ end
96
+
97
+ SPECIALS=[:top_module,:exec,:gemspec]
98
+ def create_file file
99
+
100
+ gen_file=(SPECIALS.include?(file)) ? project_name : file
101
+ ext=case file
102
+ when :exec then ""
103
+ when :gemspec then ".gemspec"
104
+ else ".rb"
105
+ end
106
+ item 1,"creating file '#{gen_file}'"
107
+ template=load_template(file)
108
+ renderer=ERB.new(template)
109
+ gen_code=renderer.result(binding)
110
+ code=Code.new(gen_code)
111
+ code.save_as "#{gen_file}#{ext}",verbose=false
112
+ end
113
+
114
+ def load_template file
115
+ template="#{__dir__}/../templates/#{file}.erb"
116
+ template_str=IO.read template
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,71 @@
1
+ require "optparse"
2
+ require "colorize"
3
+
4
+ require_relative "compiler"
5
+
6
+ module Scrag
7
+
8
+ class Runner
9
+
10
+ def self.run *arguments
11
+ new.run(arguments)
12
+ end
13
+
14
+ def run arguments
15
+ compiler=Compiler.new
16
+ compiler.options = options = parse_options(arguments)
17
+ if project=options[:project_name]
18
+ compiler.compile project
19
+ end
20
+ end
21
+
22
+ def header
23
+ puts "Scrag : scaffolding Ruby compilers (#{VERSION})- (c) JC Le Lann 20"
24
+ end
25
+
26
+ private
27
+ def parse_options(arguments)
28
+ header
29
+
30
+ parser = OptionParser.new
31
+
32
+ options = {}
33
+
34
+ parser.on("-h", "--help", "Show help message") do
35
+ puts parser
36
+ print_help_message
37
+ exit(true)
38
+ end
39
+
40
+ parser.on("--vv", "verbose") do
41
+ options[:verbose] = true
42
+ end
43
+
44
+ parser.on("--force", "force creation, even if file structure already exists") do
45
+ options[:force] = true
46
+ end
47
+
48
+ parser.on("-v", "--version", "Show version number") do
49
+ puts VERSION
50
+ exit(true)
51
+ end
52
+
53
+ parser.parse!(arguments)
54
+
55
+ options[:project_name]=arguments.shift #the remaining c file
56
+
57
+ unless options[:project_name]
58
+ puts parser
59
+ print_help_message
60
+ end
61
+
62
+ options
63
+ end
64
+
65
+ def print_help_message
66
+ puts
67
+ puts "simply enter your project name like > scrag my_dsl".green
68
+ puts
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module Scrag
2
+ VERSION="0.0.1"
3
+ end
data/lib/scrag.rb ADDED
@@ -0,0 +1,6 @@
1
+ require_relative './scrag/runner'
2
+ require_relative './scrag/version'
3
+ require_relative './scrag/compiler'
4
+
5
+ module Scrag
6
+ end
@@ -0,0 +1,42 @@
1
+ # your project process may use rkgen for class generation :
2
+ # require_relative "ast_<%=project_name%>_rkgen"
3
+
4
+ module <%=project_name.capitalize%>
5
+
6
+ class AstNode
7
+ def accept(visitor, arg=nil)
8
+ name = self.class.name.split(/::/).last
9
+ visitor.send("visit#{name}".to_sym, self ,arg) # Metaprograming !
10
+ end
11
+
12
+ def str
13
+ ppr=PrettyPrinter.new
14
+ self.accept(ppr)
15
+ end
16
+ end
17
+
18
+ class Root < AstNode
19
+ attr_accessor :design_units
20
+ def initialize design_units=[]
21
+ @design_units=design_units
22
+ end
23
+ end
24
+
25
+ class Ident < AstNode
26
+ attr_accessor :token
27
+ def initialize token=nil
28
+ @token=token
29
+ end
30
+ end
31
+
32
+ class Expression
33
+ end
34
+
35
+ class Binary < Expression
36
+ attr_accessor :lhs,:op,:rhs
37
+ def initialize lhs=nil,op=nil,rhs=nil
38
+ @lhs,@op,@rhs=lhs,op,rhs
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,47 @@
1
+ class Code
2
+
3
+ attr_accessor :indent,:lines
4
+
5
+ def initialize str=nil
6
+ @lines=[]
7
+ (@lines << str) if str
8
+ @indent=0
9
+ end
10
+
11
+ def <<(thing)
12
+ if (code=thing).is_a? Code
13
+ code.lines.each do |line|
14
+ @lines << " "*@indent+line.to_s
15
+ end
16
+ elsif thing.is_a? Array
17
+ thing.each do |kode|
18
+ @lines << kode
19
+ end
20
+ elsif thing.nil?
21
+ else
22
+ @lines << " "*@indent+thing.to_s
23
+ end
24
+ end
25
+
26
+ def finalize
27
+ return @lines.join("\n") if @lines.any?
28
+ ""
29
+ end
30
+
31
+ def newline
32
+ @lines << " "
33
+ end
34
+
35
+ def save_as filename,verbose=true,sep="\n"
36
+ str=self.finalize
37
+ File.open(filename,'w'){|f| f.puts(str)}
38
+ puts "=> code saved as : #{filename}" if verbose
39
+ return filename
40
+ end
41
+
42
+ def size
43
+ @lines.size
44
+ end
45
+
46
+
47
+ end
@@ -0,0 +1,28 @@
1
+ require 'erb'
2
+ require_relative 'parser'
3
+ require_relative 'pretty_printer'
4
+ require_relative 'transformer'
5
+ require_relative 'code'
6
+
7
+ module <%=project_name.capitalize%>
8
+
9
+ class Compiler
10
+
11
+ attr_accessor :options
12
+ attr_accessor :project_name
13
+
14
+ def initialize options={}
15
+ @options=options
16
+ end
17
+
18
+ def compile filename
19
+ puts "=> compiling #{filename}"
20
+ # parse(filename)
21
+ end
22
+
23
+ def parse filename
24
+ @ast=Parser.new.parse filename
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/<%=project_name%>'
4
+ <%= project_name.capitalize%>::Runner.run(*ARGV)
@@ -0,0 +1,33 @@
1
+ require_relative "./lib/<%=project_name%>/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = '<%=project_name%>'
5
+ s.version = <%project_name.capitalize%>::VERSION
6
+ s.date = Time.now.strftime("%Y-%m-%d")
7
+ s.summary = "<%= project_name%> DSL compiler"
8
+ s.description = "<%= project_name%> DSL compiler"
9
+ s.authors = ["Jean-Christophe Le Lann"]
10
+ s.email = 'lelannje@ensta-bretagne.fr'
11
+ s.files = [
12
+ "lib/<%=project_name%>/runner.rb",
13
+ "lib/<%=project_name%>/compiler.rb",
14
+ "lib/<%=project_name%>/ast.rb",
15
+ "lib/<%=project_name%>/code.rb",
16
+ "lib/<%=project_name%>/generic_lexer.rb",
17
+ "lib/<%=project_name%>/generic_parser.rb",
18
+ "lib/<%=project_name%>/lexer.rb",
19
+ "lib/<%=project_name%>/parser.rb",
20
+ "lib/<%=project_name%>/pretty_printer.rb",
21
+ "lib/<%=project_name%>/token.rb",
22
+ "lib/<%=project_name%>/version.rb",
23
+ "lib/<%=project_name%>.rb"
24
+ ]
25
+
26
+ s.executables << '<%=project_name%>'
27
+ s.homepage = 'https://github.com/JC-LL/<%=project_name%>'
28
+ s.license = 'MIT'
29
+ s.post_install_message = "Thanks for installing ! Homepage :https://github.com/JC-LL/<%=project_name%>"
30
+ s.required_ruby_version = '>= 2.0.0'
31
+ s.add_runtime_dependency 'colorize', '0.8.1'
32
+
33
+ end
@@ -0,0 +1,62 @@
1
+ require 'strscan'
2
+
3
+ require_relative 'token'
4
+
5
+ class GenericLexer
6
+
7
+ def initialize
8
+ @rules = []
9
+ @rules << [:newline,/[\n]/]
10
+ end
11
+
12
+ def ignore pattern
13
+ @rules << [:skip,pattern]
14
+ end
15
+
16
+ def keyword str
17
+ @rules.unshift [str.to_sym,/#{str}\b/i]
18
+ end
19
+
20
+ def token hash
21
+ token,pattern=*hash.to_a.flatten
22
+ @rules << [token, pattern]
23
+ end
24
+
25
+ def open code
26
+ @ssc = StringScanner.new code
27
+ @line=0
28
+ end
29
+
30
+ def next_token
31
+ return [nil,nil,nil] if @ssc.empty?
32
+ tok = get_token
33
+ return (tok.is_a? :skip) ? next_token : tok
34
+ end
35
+
36
+ def get_token
37
+ linecol=position()
38
+ @rules.each do |rule, regexp|
39
+ val = @ssc.scan(regexp)
40
+ return Token.new([rule, val, linecol]) if val
41
+ end
42
+ raise "lexing error line #{linecol.first} around : ...'#{@ssc.peek(5)}'... "
43
+ end
44
+
45
+ def position
46
+ if @ssc.bol?
47
+ @line+=1
48
+ @old_pos=@ssc.pos
49
+ end
50
+ [@line,@ssc.pos-@old_pos+1]
51
+ end
52
+
53
+ def tokenize code
54
+ open(code)
55
+ tokens=[]
56
+ tokens << next_token() while not @ssc.eos?
57
+ # while not @ssc.eos?
58
+ # tokens << (p next_token)
59
+ # end #usefull for debug
60
+ tokens
61
+ end
62
+ end
@@ -0,0 +1,53 @@
1
+ class GenericParser
2
+
3
+ def acceptIt
4
+ tok=tokens.shift
5
+ puts "consuming #{tok.val} (#{tok.kind})" if @verbose
6
+ tok
7
+ end
8
+
9
+ def showNext k=1
10
+ tokens[k-1]
11
+ end
12
+
13
+ def expect kind
14
+ if (actual=showNext.kind)!=kind
15
+ abort "ERROR at #{showNext.pos}. Expecting #{kind}. Got #{actual}"
16
+ else
17
+ return acceptIt()
18
+ end
19
+ end
20
+
21
+ def maybe kind
22
+ if showNext.kind==kind
23
+ return acceptIt
24
+ end
25
+ nil
26
+ end
27
+
28
+ def more?
29
+ !tokens.empty?
30
+ end
31
+
32
+ def lookahead n
33
+ showNext(k=n)
34
+ end
35
+
36
+ def niy
37
+ raise "NIY"
38
+ end
39
+
40
+ def next_tokens n=5
41
+ @tokens[0..n].map{|tok| [tok.kind,tok.val].to_s}.join(',')
42
+ end
43
+
44
+ def consume_to token_kind
45
+ while showNext && showNext.kind!=token_kind
46
+ acceptIt
47
+ end
48
+ if showNext.nil?
49
+ raise "cannot find token '#{token_kind}'"
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,50 @@
1
+ require_relative 'generic_lexer'
2
+ require_relative 'generic_parser'
3
+
4
+ module <%=project_name.capitalize%>
5
+ class Lexer < GenericLexer
6
+ def initialize
7
+ super
8
+
9
+ keyword 'if'
10
+
11
+ #.............................................................
12
+ token :comment => /\A\-\-(.*)$/
13
+ token :ident => /[a-zA-Z]\w*/
14
+ token :string_literal => /"[^"]*"/
15
+ token :char_literal => /'(\w+)'/
16
+ token :decimal_literal => /\d+(\.\d+)?(E([+-]?)\d+)?/
17
+
18
+ token :comma => /\A\,/
19
+ token :colon => /\A\:/
20
+ token :semicolon => /\A\;/
21
+ token :lparen => /\A\(/
22
+ token :rparen => /\A\)/
23
+
24
+ # arith
25
+ token :add => /\A\+/
26
+ token :sub => /\A\-/
27
+ token :mul => /\A\*/
28
+ token :div => /\A\//
29
+
30
+ token :imply => /\A\=\>/
31
+
32
+ # logical
33
+ token :eq => /\A\=/
34
+ token :neq => /\A\/\=/
35
+ token :gte => /\A\>\=/
36
+ token :gt => /\A\>/
37
+ token :leq => /\A\<\=/
38
+ token :lt => /\A\</
39
+
40
+ token :ampersand => /\A\&/
41
+
42
+ token :dot => /\A\./
43
+ token :bar => /\|/
44
+ #............................................................
45
+ token :newline => /[\n]/
46
+ token :space => /[ \t\r]+/
47
+
48
+ end #def
49
+ end #class
50
+ end #module
@@ -0,0 +1,73 @@
1
+ # coding: utf-8
2
+ require_relative 'generic_parser'
3
+ require_relative 'ast'
4
+ require_relative 'lexer'
5
+
6
+ module <%=project_name.capitalize%>
7
+
8
+ class Parser < GenericParser
9
+
10
+ attr_accessor :options
11
+ attr_accessor :lexer,:tokens
12
+ attr_accessor :basename,:filename
13
+
14
+ def initialize options={}
15
+ @options=options
16
+ end
17
+
18
+ def lex filename
19
+ unless File.exists?(filename)
20
+ raise "ERROR : cannot find file '#{filename}'"
21
+ end
22
+ begin
23
+ str=IO.read(filename).downcase
24
+ tokens=Lexer.new.tokenize(str)
25
+ tokens=tokens.select{|t| t.class==Token} # filters [nil,nil,nil]
26
+ tokens.reject!{|tok| tok.is_a? [:comment,:newline,:space]}
27
+ return tokens
28
+ rescue Exception=>e
29
+ unless options[:mute]
30
+ puts e.backtrace
31
+ puts e
32
+ end
33
+ raise "an error occured during LEXICAL analysis. Sorry. Aborting."
34
+ end
35
+ end
36
+
37
+ def parse filename
38
+ begin
39
+ @tokens=lex(filename)
40
+ puts "......empty file !" if tokens.size==0
41
+ root=Root.new([])
42
+ while @tokens.any?
43
+ case showNext.kind
44
+ when :comment
45
+ root << acceptIt
46
+ # etc...
47
+ else
48
+ raise "got #{showNext}"
49
+ end
50
+ end
51
+ rescue Exception => e
52
+ unless options[:mute]
53
+ puts e.backtrace
54
+ puts e
55
+ end
56
+ raise
57
+ end
58
+ root
59
+ end
60
+
61
+ def parse_thing
62
+ expect :thing
63
+ expect :ident
64
+ while showNext.is_a?(:comma)
65
+ acceptIt
66
+ expect :ident
67
+ end
68
+ expect :semicolon
69
+ end
70
+
71
+ # ....etc...
72
+ end
73
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'code'
2
+
3
+ module <%=project_name.capitalize%>
4
+
5
+ class PrettyPrinter
6
+
7
+ def print ast
8
+ ast.accept(self)
9
+ end
10
+
11
+ def visitRoot root,args=nil
12
+ code=Code.new
13
+ code << "--automatically generated"
14
+ root.list.each{|e| code << e.accept(self)}
15
+ code
16
+ end
17
+
18
+ def visitComment comment,args=nil
19
+ code=Code.new
20
+ comment.list.each{|e| code << e.accept(self)}
21
+ code
22
+ end
23
+
24
+ def visitIdent id,args=nil
25
+ id.token.val
26
+ end
27
+
28
+ end
29
+ end