scrag 0.0.1

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