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 +7 -0
- data/bin/scrag +4 -0
- data/lib/scrag/code.rb +47 -0
- data/lib/scrag/compiler.rb +119 -0
- data/lib/scrag/runner.rb +71 -0
- data/lib/scrag/version.rb +3 -0
- data/lib/scrag.rb +6 -0
- data/lib/templates/ast.erb +42 -0
- data/lib/templates/code.erb +47 -0
- data/lib/templates/compiler.erb +28 -0
- data/lib/templates/exec.erb +4 -0
- data/lib/templates/gemspec.erb +33 -0
- data/lib/templates/generic_lexer.erb +62 -0
- data/lib/templates/generic_parser.erb +53 -0
- data/lib/templates/lexer.erb +50 -0
- data/lib/templates/parser.erb +73 -0
- data/lib/templates/pretty_printer.erb +29 -0
- data/lib/templates/runner.erb +97 -0
- data/lib/templates/token.erb +63 -0
- data/lib/templates/top_module.erb +7 -0
- data/lib/templates/transformer.erb +487 -0
- data/lib/templates/version.erb +3 -0
- data/lib/templates/visitor.erb +39 -0
- metadata +79 -0
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
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
|
data/lib/scrag/runner.rb
ADDED
@@ -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
|
data/lib/scrag.rb
ADDED
@@ -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,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
|