oryx 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -0
- data/README.md +27 -7
- data/Rakefile +2 -0
- data/bin/oryx +6 -0
- data/bin/oryx.markdown +29 -0
- data/lib/oryx.rb +4 -0
- data/lib/oryx/lexer.rb +92 -0
- data/lib/oryx/options.rb +51 -0
- data/lib/oryx/preprocessor.rb +21 -0
- data/lib/oryx/runner.rb +56 -0
- data/lib/oryx/version.rb +1 -1
- data/man/man1/oryx.1 +21 -0
- data/oryx.gemspec +12 -4
- data/test/lib/oryx/lexer_test.rb +303 -0
- data/test/lib/oryx/preprocessor_test.rb +65 -0
- data/test/lib/oryx/version_test.rb +12 -0
- data/test/test_helper.rb +5 -0
- metadata +115 -11
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
# Oryx
|
2
|
+
C-Flat to x86 compiler.
|
2
3
|
|
3
|
-
|
4
|
+
## Progress
|
5
|
+
|
6
|
+
- 20 Feb 13
|
7
|
+
+ Oryx 0.1.0
|
8
|
+
+ Lexer complete
|
9
|
+
|
10
|
+
## C-Flat
|
11
|
+
C-Flat is a working subset of C designed for use in compilers courses. C-flat includes expressions, basic control flow, recursive functions, and strict type checking. It is object-compatible with ordinary C and thus can take advantage of the standard C library, at least when using limited types.
|
4
12
|
|
5
13
|
## Installation
|
6
14
|
|
@@ -18,12 +26,24 @@ Or install it yourself as:
|
|
18
26
|
|
19
27
|
## Usage
|
20
28
|
|
21
|
-
|
29
|
+
$ oryx ~/test.cflat
|
30
|
+
|
31
|
+
For more details try
|
32
|
+
|
33
|
+
$ oryx -h
|
34
|
+
|
35
|
+
or
|
36
|
+
|
37
|
+
$ man oryx
|
22
38
|
|
23
39
|
## Contributing
|
40
|
+
As this is a course project pull requests will be ignored.
|
41
|
+
|
42
|
+
## Build Status
|
43
|
+
[![Build Status](https://travis-ci.org/rampantmonkey/oryx.png?branch=master)](https://travis-ci.org/rampantmonkey/oryx)
|
44
|
+
|
45
|
+
Provided by [Travis-CI](http://travis-ci.org)
|
46
|
+
|
47
|
+
## License
|
24
48
|
|
25
|
-
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
5. Create new Pull Request
|
49
|
+
MIT
|
data/Rakefile
CHANGED
data/bin/oryx
ADDED
data/bin/oryx.markdown
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
=begin===
|
2
|
+
# ORYX 2013-2-20 0.1.0
|
3
|
+
|
4
|
+
## NAME
|
5
|
+
|
6
|
+
oryx - cflat tokenizer
|
7
|
+
|
8
|
+
## SYNOPSIS
|
9
|
+
|
10
|
+
`oryx` [-o output-file] input-file
|
11
|
+
|
12
|
+
## DESCRIPTION
|
13
|
+
|
14
|
+
[oryx] produces a table of tokens representing the source code contained in `input-file`. If invalid characters/strings are found they are returned as error tokens and lexing continues. Oryx can even handle (in other words not throw an exception) when given random input, as shown by the test suite.
|
15
|
+
|
16
|
+
## OPTIONS
|
17
|
+
|
18
|
+
`-h`, `--help`
|
19
|
+
Show list of options and usage
|
20
|
+
|
21
|
+
`-o`, `--output` [*FILE*]
|
22
|
+
Specify the filename in which to write the token table
|
23
|
+
|
24
|
+
|
25
|
+
## SEE ALSO
|
26
|
+
|
27
|
+
clang(1), gcc(1)
|
28
|
+
|
29
|
+
=end===
|
data/lib/oryx.rb
CHANGED
data/lib/oryx/lexer.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rltk'
|
2
|
+
|
3
|
+
module Oryx
|
4
|
+
class Lexer < RLTK::Lexer
|
5
|
+
# Skip whitespace
|
6
|
+
rule(/\s/)
|
7
|
+
|
8
|
+
# Keywords
|
9
|
+
rule(/boolean/) { :BOOLEAN }
|
10
|
+
rule(/char/) { :CHAR }
|
11
|
+
rule(/else/) { :ELSE }
|
12
|
+
rule(/false/) { :FALSE }
|
13
|
+
rule(/if/) { :IF }
|
14
|
+
rule(/int/) { :INT }
|
15
|
+
rule(/print/) { :PRINT }
|
16
|
+
rule(/return/) { :RETURN }
|
17
|
+
rule(/string/) { :STRING }
|
18
|
+
rule(/true/) { :TRUE }
|
19
|
+
rule(/void/) { :VOID }
|
20
|
+
rule(/while/) { :WHILE }
|
21
|
+
|
22
|
+
# Operators and delimiters
|
23
|
+
rule(/\(/) { :LPAREN }
|
24
|
+
rule(/\)/) { :RPAREN }
|
25
|
+
rule(/\{/) { :LCURLY }
|
26
|
+
rule(/\}/) { :RCURLY }
|
27
|
+
rule(/\*/) { :TIMES }
|
28
|
+
rule(/\//) { :DIV }
|
29
|
+
rule(/\+/) { :PLUS }
|
30
|
+
rule(/-/) { :MINUS }
|
31
|
+
rule(/</) { :LE }
|
32
|
+
rule(/<=/) { :LEQ }
|
33
|
+
rule(/>=/) { :GEQ }
|
34
|
+
rule(/>/) { :GE }
|
35
|
+
rule(/==/) { :EQ }
|
36
|
+
rule(/\!=/) { :NEQ }
|
37
|
+
rule(/&&/) { :AND }
|
38
|
+
rule(/\|\|/) { :OR }
|
39
|
+
rule(/=/) { :ASSIGN }
|
40
|
+
rule(/;/) { :SEMI }
|
41
|
+
rule(/,/) { :COMMA }
|
42
|
+
|
43
|
+
# Continue Line
|
44
|
+
rule(/\\[\ \t]*\n/) { :CONTNL }
|
45
|
+
|
46
|
+
# Identifier
|
47
|
+
rule(/^[^\d\W]\w*/) { |t| [:IDENT, t] }
|
48
|
+
|
49
|
+
# Numerics
|
50
|
+
rule(/\d+/) { |t| [:NUM, t] }
|
51
|
+
|
52
|
+
# Comments
|
53
|
+
rule(/\/\//) { push_state :cpp_comment }
|
54
|
+
rule(/\n/, :cpp_comment) { pop_state }
|
55
|
+
rule(/./, :cpp_comment)
|
56
|
+
rule(/\/\*/) { push_state :c_comment; set_flag :c_comment}
|
57
|
+
rule(/\*\//, :c_comment) { pop_state; unset_flag :c_comment }
|
58
|
+
rule(/\*\//) { :UCOMTER }
|
59
|
+
rule(/\n/, :c_comment)
|
60
|
+
rule(/./, :c_comment)
|
61
|
+
|
62
|
+
# Characters
|
63
|
+
rule(/\'((\\(n|0))|[^\']{1})\'/) { |t| [:CHARCON, t[1...-1]] }
|
64
|
+
rule(/\'\\[^n0]{1}'/) { |t| [:CHARCON, t[2]] }
|
65
|
+
rule(/\'[^\']*'/) { |t| [:INVCON, t[1...-1]] }
|
66
|
+
|
67
|
+
# Strings
|
68
|
+
rule(/\"/) { push_state :string }
|
69
|
+
rule(/\n/, :string) { pop_state; :MSTRTER }
|
70
|
+
rule(/(\\\"|[^\"\n]){,255}/, :string) do |t|
|
71
|
+
push_state :str_overflow if t.length == 255
|
72
|
+
[:STRCON, t[0...255]]
|
73
|
+
end
|
74
|
+
rule(/[^\"]*/, :str_overflow) { :STRLNG }
|
75
|
+
rule(/\"/, :str_overflow) { pop_state }
|
76
|
+
rule(/\"/, :string) { pop_state }
|
77
|
+
|
78
|
+
# Invalid token starters
|
79
|
+
rule(/[^(\w|\s|\(|\)|\{|\}|\*|\/|\+|-|<|>|=|\!|&|\||;|,)\"|\'|\\]/) { |t| [:INVCHR, t] }
|
80
|
+
|
81
|
+
# Pokémon Rule
|
82
|
+
rule(/./) { |t| [:INVCHR, t]}
|
83
|
+
|
84
|
+
def lex string, file_name = nil
|
85
|
+
tokens = super
|
86
|
+
if env.flags.include? :c_comment
|
87
|
+
tokens.insert -2, RLTK::Token.new(:MCOMTER)
|
88
|
+
end
|
89
|
+
tokens
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/oryx/options.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Oryx
|
5
|
+
class Options
|
6
|
+
DEFAULT_INPUT = ""
|
7
|
+
DEFAULT_OUTPUT = Pathname.new("/tmp/oryx-output")
|
8
|
+
|
9
|
+
def initialize(argv)
|
10
|
+
@config = {output: DEFAULT_OUTPUT,
|
11
|
+
input: DEFAULT_INPUT}
|
12
|
+
parse(argv)
|
13
|
+
begin
|
14
|
+
input = Pathname.new(argv.pop).expand_path
|
15
|
+
@config[:input] = input
|
16
|
+
rescue TypeError => e
|
17
|
+
STDERR.puts "No input_file specified.\nUse --help for more information."
|
18
|
+
exit(-1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing m, *args, &block
|
23
|
+
if @config.has_key? m
|
24
|
+
@config[m]
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse(argv)
|
31
|
+
OptionParser.new do |opts|
|
32
|
+
opts.banner = "Usage: oryx [ options ] input_file"
|
33
|
+
opts.on("-h", "--help", "Show this message") do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
opts.on("-o", "--output FILE", String, "Output filename") do |path|
|
38
|
+
@config[:output] = Pathname.new(path).expand_path
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
argv = ["-h"] if argv.empty?
|
43
|
+
opts.parse!(argv)
|
44
|
+
rescue OptionParser::ParseError => e
|
45
|
+
STDERR.puts e.message, "\n", opts
|
46
|
+
exit(-1)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Oryx
|
2
|
+
class Preprocessor
|
3
|
+
def initialize
|
4
|
+
end
|
5
|
+
|
6
|
+
def parse content
|
7
|
+
content = remove_comment content
|
8
|
+
remove_outer_whitespace content
|
9
|
+
end
|
10
|
+
|
11
|
+
def remove_comment content
|
12
|
+
content.gsub /\/\*.*?\*\//, ''
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_outer_whitespace content
|
16
|
+
content.lstrip.rstrip
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/oryx/runner.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative '../oryx.rb'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module Oryx
|
5
|
+
class Runner
|
6
|
+
attr_reader :input_filename, :output_filename
|
7
|
+
|
8
|
+
def initialize argv
|
9
|
+
@options = Options.new argv
|
10
|
+
@input_filename = @options.input
|
11
|
+
@output_filename = @options.output
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
puts "input: #{input_filename}"
|
16
|
+
puts "output: #{output_filename}"
|
17
|
+
print "lexing".blue+"."*17
|
18
|
+
l = Lexer.new
|
19
|
+
output_filename.open('w:UTF-8') { |f| f.write(tabularize_output l.lex_file(input_filename.to_s)) }
|
20
|
+
puts "complete".green
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def tabularize_output tokens
|
25
|
+
s = table_header
|
26
|
+
tokens.each do |t|
|
27
|
+
line = ""
|
28
|
+
line += format " %-9s", t.type
|
29
|
+
line += " "
|
30
|
+
line += center t.value.to_s, 10
|
31
|
+
line += " "
|
32
|
+
line += center("#{t.position.line_number},#{t.position.line_offset}", 13) if t.position
|
33
|
+
s += line + "\n"
|
34
|
+
end
|
35
|
+
s
|
36
|
+
end
|
37
|
+
|
38
|
+
def table_header
|
39
|
+
s = center "TYPE", 10
|
40
|
+
s += "|"
|
41
|
+
s += center "VALUE", 10
|
42
|
+
s += "|"
|
43
|
+
s += center "POSITION", 12
|
44
|
+
s += "\n"
|
45
|
+
s += "-"*35
|
46
|
+
s += "\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def center text, width
|
51
|
+
left = (width - text.length)/2
|
52
|
+
right = width - text.length - left
|
53
|
+
" "*left + text + " "*right
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/oryx/version.rb
CHANGED
data/man/man1/oryx.1
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
.TH ORYX 2013\-2\-20 0.1.0
|
2
|
+
.SH NAME
|
3
|
+
.PP
|
4
|
+
oryx \- cflat tokenizer
|
5
|
+
.SH SYNOPSIS
|
6
|
+
.PP
|
7
|
+
\fB\fCoryx\fR [\-o output\-file] input\-file
|
8
|
+
.SH DESCRIPTION
|
9
|
+
.PP
|
10
|
+
[oryx] produces a table of tokens representing the source code contained in \fB\fCinput-file\fR. If invalid characters/strings are found they are returned as error tokens and lexing continues. Oryx can even handle (in other words not throw an exception) when given random input, as shown by the test suite.
|
11
|
+
.SH OPTIONS
|
12
|
+
.PP
|
13
|
+
\fB\fC-h\fR, \fB\fC--help\fR
|
14
|
+
Show list of options and usage
|
15
|
+
.PP
|
16
|
+
\fB\fC-o\fR, \fB\fC--output\fR [\fIFILE\fP]
|
17
|
+
Specify the filename in which to write the token table
|
18
|
+
.SH SEE ALSO
|
19
|
+
.PP
|
20
|
+
.BR clang (1),
|
21
|
+
.BR gcc (1)
|
data/oryx.gemspec
CHANGED
@@ -6,16 +6,24 @@ require 'oryx/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "oryx"
|
8
8
|
gem.version = Oryx::VERSION
|
9
|
+
gem.date = Date.today.to_s
|
9
10
|
gem.authors = ["Casey Robinson"]
|
10
11
|
gem.email = ["kc@rampantmonkey.com"]
|
11
|
-
gem.description = %q{
|
12
|
-
gem.summary = %q{
|
13
|
-
gem.homepage = ""
|
12
|
+
gem.description = %q{C-Flat to x86 compiler}
|
13
|
+
gem.summary = %q{Take a program written in C-Flat and convert it to x86 assembly}
|
14
|
+
gem.homepage = "http://github.com/rampantmonkey/oryx"
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.files += Dir['man/man?/*.?']
|
16
18
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
19
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
20
|
gem.require_paths = ["lib"]
|
19
21
|
|
20
|
-
gem.add_development_dependency 'rake'
|
22
|
+
gem.add_development_dependency 'rake', '~>0.9.2.2'
|
23
|
+
gem.add_development_dependency 'shoulda', '~>3.3.2'
|
24
|
+
gem.add_development_dependency 'md2man', '~>1.4'
|
25
|
+
gem.add_development_dependency 'binman', '~>3.2.0'
|
26
|
+
|
27
|
+
gem.add_dependency 'rltk', '~>2.2.1'
|
28
|
+
gem.add_dependency 'colorize', '~>0.5.8'
|
21
29
|
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
require 'rltk'
|
3
|
+
|
4
|
+
module Oryx
|
5
|
+
class TestLexer < Test::Unit::TestCase
|
6
|
+
def compare(input, expected)
|
7
|
+
l = Lexer.new
|
8
|
+
l.lex(input).each_with_index do |t, i|
|
9
|
+
assert_equal expected[i], t.to_s unless t.to_s == "EOS"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid_id? query_id
|
14
|
+
assert is_id? query_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def invalid_id? query_id
|
18
|
+
assert !is_id?(query_id), "#{query_id} is a valid id"
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_id? query_id
|
22
|
+
l = Lexer.new
|
23
|
+
tokens = l.lex(query_id).map{|t| ["IDENT", "EOS"].include? t.type.to_s }
|
24
|
+
!tokens.include? false
|
25
|
+
end
|
26
|
+
|
27
|
+
context "keywords" do
|
28
|
+
should "match boolean" do
|
29
|
+
compare("boolean", ["BOOLEAN"])
|
30
|
+
end
|
31
|
+
should "match char" do
|
32
|
+
compare("char", ["CHAR"])
|
33
|
+
end
|
34
|
+
should "match else" do
|
35
|
+
compare("else", ["ELSE"])
|
36
|
+
end
|
37
|
+
should "match false" do
|
38
|
+
compare("false", ["FALSE"])
|
39
|
+
end
|
40
|
+
should "match if" do
|
41
|
+
compare("if", ["IF"])
|
42
|
+
end
|
43
|
+
should "match int" do
|
44
|
+
compare("int", ["INT"])
|
45
|
+
end
|
46
|
+
should "match print" do
|
47
|
+
compare("print", ["PRINT"])
|
48
|
+
end
|
49
|
+
should "match return" do
|
50
|
+
compare("return", ["RETURN"])
|
51
|
+
end
|
52
|
+
should "match string" do
|
53
|
+
compare("string", ["STRING"])
|
54
|
+
end
|
55
|
+
should "match true" do
|
56
|
+
compare("true", ["TRUE"])
|
57
|
+
end
|
58
|
+
should "match void" do
|
59
|
+
compare("void", ["VOID"])
|
60
|
+
end
|
61
|
+
should "match while" do
|
62
|
+
compare("while", ["WHILE"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "operators and delimiters" do
|
67
|
+
should "match )" do
|
68
|
+
compare('(', ["LPAREN"])
|
69
|
+
end
|
70
|
+
should "match (" do
|
71
|
+
compare(')', ["RPAREN"])
|
72
|
+
end
|
73
|
+
should "match {" do
|
74
|
+
compare('{', ["LCURLY"])
|
75
|
+
end
|
76
|
+
should "match }" do
|
77
|
+
compare('}', ["RCURLY"])
|
78
|
+
end
|
79
|
+
should "match *" do
|
80
|
+
compare('*', ["TIMES"])
|
81
|
+
end
|
82
|
+
should "match " do
|
83
|
+
compare('/', ["DIV"])
|
84
|
+
end
|
85
|
+
should "match +" do
|
86
|
+
compare('+', ["PLUS"])
|
87
|
+
end
|
88
|
+
should "match -" do
|
89
|
+
compare('-', ["MINUS"])
|
90
|
+
end
|
91
|
+
should "match <" do
|
92
|
+
compare('<', ["LE"])
|
93
|
+
end
|
94
|
+
should "match <=" do
|
95
|
+
compare('<=', ["LEQ"])
|
96
|
+
end
|
97
|
+
should "match >=" do
|
98
|
+
compare('>=', ["GEQ"])
|
99
|
+
end
|
100
|
+
should "match >" do
|
101
|
+
compare('>', ["GE"])
|
102
|
+
end
|
103
|
+
should "match ==" do
|
104
|
+
compare('==', ["EQ"])
|
105
|
+
end
|
106
|
+
should "match !=" do
|
107
|
+
compare('!=', ["NEQ"])
|
108
|
+
end
|
109
|
+
should "match &&" do
|
110
|
+
compare('&&', ["AND"])
|
111
|
+
end
|
112
|
+
should "match ||" do
|
113
|
+
compare('||', ["OR"])
|
114
|
+
end
|
115
|
+
should "match =" do
|
116
|
+
compare('=', ["ASSIGN"])
|
117
|
+
end
|
118
|
+
should "match ;" do
|
119
|
+
compare(';', ["SEMI"])
|
120
|
+
end
|
121
|
+
should "match ," do
|
122
|
+
compare(',', ["COMMA"])
|
123
|
+
end
|
124
|
+
should "match newline" do
|
125
|
+
queries = [ "\\\n",
|
126
|
+
"\\\t\n",
|
127
|
+
"\\ \n"]
|
128
|
+
queries.each { |q| compare q, ["CONTNL"] }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "identifiers" do
|
133
|
+
should "be valid ids" do
|
134
|
+
queries = [ "a",
|
135
|
+
"a1",
|
136
|
+
"qwerty_uiop",
|
137
|
+
"abc123",
|
138
|
+
"ab_12",
|
139
|
+
"etc",
|
140
|
+
"j",
|
141
|
+
"LastNumber",
|
142
|
+
"not_sureHow_many_more_areNecessary"]
|
143
|
+
queries.each { |q| valid_id? q }
|
144
|
+
end
|
145
|
+
|
146
|
+
should "be invalid ids" do
|
147
|
+
queries = [ "if",
|
148
|
+
'?',
|
149
|
+
"1a",
|
150
|
+
"1.ab",
|
151
|
+
"1b a",
|
152
|
+
"ab 12",
|
153
|
+
"abc.126",
|
154
|
+
"!abc123"]
|
155
|
+
queries.each { |q| invalid_id? q }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "comments" do
|
160
|
+
should "remove the c-style commment" do
|
161
|
+
compare "/* this is a comment */", ["EOS"]
|
162
|
+
end
|
163
|
+
|
164
|
+
should "remove two line c-style comment" do
|
165
|
+
compare "/* line 1 \n line 2 */", ["EOS"]
|
166
|
+
end
|
167
|
+
|
168
|
+
should "remove multi-line comment (c-style)" do
|
169
|
+
input = "/*" + "dnfdikas kenib UHJa ia !( *(*#ASD}{asdfi%*&) \n"*50 + "*/"
|
170
|
+
compare input, ["EOS"]
|
171
|
+
end
|
172
|
+
|
173
|
+
should "remove c++-style comment" do
|
174
|
+
compare "//\n", ["EOS"]
|
175
|
+
end
|
176
|
+
|
177
|
+
should "return warning with eos in comment" do
|
178
|
+
compare "/*", ["MCOMTER"]
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
context "test.cflat" do
|
184
|
+
should "replicate results from example" do
|
185
|
+
input = "char true boolean\nboolean ( +\nstring s"
|
186
|
+
result = ["CHAR", "TRUE", "BOOLEAN", "BOOLEAN", "LPAREN", "PLUS", "STRING", "IDENT(s)"]
|
187
|
+
compare input, result
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "comments" do
|
192
|
+
should "produce error token" do
|
193
|
+
compare '*/', ["UCOMTER"]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "characters" do
|
198
|
+
should "produce a character constant token" do
|
199
|
+
compare "'a'", ["CHARCON(a)"]
|
200
|
+
end
|
201
|
+
|
202
|
+
should "not be a character constant" do
|
203
|
+
compare "'abc'", ["INVCON(abc)"]
|
204
|
+
end
|
205
|
+
|
206
|
+
should "disallow empty character constants" do
|
207
|
+
compare "''", ["INVCON()"]
|
208
|
+
end
|
209
|
+
|
210
|
+
should "match escape sequences" do
|
211
|
+
valid_escapes = %w{n 0}
|
212
|
+
valid_escapes.each do |v|
|
213
|
+
sequence = "'\\#{v.lstrip}'"
|
214
|
+
result = ["CHARCON(\\#{v.lstrip})"]
|
215
|
+
compare sequence, result
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
should "return character for non-special escape sequence" do
|
220
|
+
test_escapes = %w{c d e g h ! ^ &}
|
221
|
+
test_escapes.each do |t|
|
222
|
+
sequence = "'\\#{t.lstrip}'"
|
223
|
+
result = ["CHARCON(#{t.lstrip})"]
|
224
|
+
compare sequence, result
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "strings" do
|
230
|
+
should "produce a string constant token" do
|
231
|
+
compare '"a"', ["STRCON(a)"]
|
232
|
+
end
|
233
|
+
|
234
|
+
should "produce a longer string constant" do
|
235
|
+
input = '"Why, you stuck up, half-witted, scruffy-looking Nerf herder."'
|
236
|
+
compare input, ["STRCON(#{input[1...-1]})"]
|
237
|
+
end
|
238
|
+
|
239
|
+
should "handle escaped double quote in string" do
|
240
|
+
input = '"\""'
|
241
|
+
compare input, ["STRCON(#{input[1...-1]})"]
|
242
|
+
end
|
243
|
+
|
244
|
+
should "report missing closing double quote" do
|
245
|
+
input = '"asdfasdf
|
246
|
+
'
|
247
|
+
compare input, ["STRCON(asdfasdf)", "MSTRTER"]
|
248
|
+
end
|
249
|
+
|
250
|
+
should "report that string is too long" do
|
251
|
+
input = '"' + "a"*259 + '"'
|
252
|
+
compare input, ["STRCON(#{'a'*255})", "STRLNG"]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context "fibonacci sequence example" do
|
257
|
+
should "match the expected result" do
|
258
|
+
input = <<-'EOF'
|
259
|
+
int x=35;
|
260
|
+
void fib( int x )
|
261
|
+
{
|
262
|
+
if(x<2) {
|
263
|
+
return 1;
|
264
|
+
} else {
|
265
|
+
return fib(x-1)+fib(x-2);
|
266
|
+
} }
|
267
|
+
int main() {
|
268
|
+
int i=x;
|
269
|
+
while(i>0) {
|
270
|
+
print "fib(", i, ") = ", fib(i), "\n";
|
271
|
+
i=i-1; }
|
272
|
+
return 0;
|
273
|
+
}
|
274
|
+
EOF
|
275
|
+
expected = %w{INT IDENT(x) ASSIGN NUM(35) SEMI VOID IDENT(fib) LPAREN INT
|
276
|
+
IDENT(x) RPAREN LCURLY IF LPAREN IDENT(x) LE NUM(2) RPAREN
|
277
|
+
LCURLY RETURN NUM(1) SEMI RCURLY ELSE LCURLY RETURN IDENT(fib)
|
278
|
+
LPAREN IDENT(x) MINUS NUM(1) RPAREN PLUS IDENT(fib) LPAREN
|
279
|
+
IDENT(x) MINUS NUM(2) RPAREN SEMI RCURLY RCURLY INT IDENT(main)
|
280
|
+
LPAREN RPAREN LCURLY INT IDENT(i) ASSIGN IDENT(x) SEMI WHILE
|
281
|
+
LPAREN IDENT(i) GE NUM(0) RPAREN LCURLY PRINT STRCON(fib()
|
282
|
+
COMMA IDENT(i) COMMA STRCON()\ =\ ) COMMA IDENT(fib) LPAREN
|
283
|
+
IDENT(i) RPAREN COMMA STRCON(\\n) SEMI IDENT(i) ASSIGN IDENT(i)
|
284
|
+
MINUS NUM(1) SEMI RCURLY RETURN NUM(0) SEMI RCURLY EOS}
|
285
|
+
compare input, expected
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "randomness" do
|
290
|
+
should "not raise an error" do
|
291
|
+
assert_nothing_raised do
|
292
|
+
random = `cat /dev/urandom | strings | head -100`
|
293
|
+
random.encode!("ISO-8859-1", invalid: :replace)
|
294
|
+
input = random.encode("UTF-8", invalid: :replace)
|
295
|
+
l = Lexer.new
|
296
|
+
l.lex(input)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
|
3
|
+
module Oryx
|
4
|
+
class TestPreprocessor < Test::Unit::TestCase
|
5
|
+
context "one line inputs" do
|
6
|
+
setup do
|
7
|
+
@preprocessor = Preprocessor.new
|
8
|
+
end
|
9
|
+
|
10
|
+
should "not modify empty input" do
|
11
|
+
test_input = ""
|
12
|
+
assert_equal test_input, @preprocessor.parse(test_input)
|
13
|
+
end
|
14
|
+
|
15
|
+
should "not modify simple assignment" do
|
16
|
+
test_input = "int length=7;"
|
17
|
+
assert_equal test_input, @preprocessor.parse(test_input)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "remove block comment" do
|
21
|
+
test_input = "/* this is a block comment in c */"
|
22
|
+
assert_equal "", @preprocessor.parse(test_input)
|
23
|
+
end
|
24
|
+
|
25
|
+
should "remove trailing comment" do
|
26
|
+
test_input = "int a = 5; /* a */"
|
27
|
+
assert_equal "int a = 5;", @preprocessor.parse(test_input)
|
28
|
+
end
|
29
|
+
|
30
|
+
should "remove starting comment" do
|
31
|
+
test_input = "/* a */ int a = 5;"
|
32
|
+
assert_equal "int a = 5;", @preprocessor.parse(test_input)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "remove both comments" do
|
36
|
+
test_input = "/* a */ int a = 5; /* b */"
|
37
|
+
assert_equal "int a = 5;", @preprocessor.parse(test_input)
|
38
|
+
end
|
39
|
+
|
40
|
+
should "remove tons of comments" do
|
41
|
+
test_input = "/* a */"*700 + " int a = 5; " + "/* b */"*107
|
42
|
+
assert_equal "int a = 5;", @preprocessor.parse(test_input)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "multi line test" do
|
47
|
+
setup do
|
48
|
+
@preprocessor = Preprocessor.new
|
49
|
+
end
|
50
|
+
|
51
|
+
should "remove extra newline" do
|
52
|
+
test_input = "\n"
|
53
|
+
assert_equal "", @preprocessor.parse(test_input)
|
54
|
+
end
|
55
|
+
|
56
|
+
should "do nothing" do
|
57
|
+
test_input = "int a=5;\na++"
|
58
|
+
assert_equal test_input, @preprocessor.parse(test_input)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oryx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,40 +9,134 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 0.9.2.2
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
|
29
|
+
version: 0.9.2.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: shoulda
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.3.2
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.3.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: md2man
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.4'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.4'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: binman
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.2.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.2.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rltk
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.2.1
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 2.2.1
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: colorize
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.5.8
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.5.8
|
110
|
+
description: C-Flat to x86 compiler
|
31
111
|
email:
|
32
112
|
- kc@rampantmonkey.com
|
33
|
-
executables:
|
113
|
+
executables:
|
114
|
+
- oryx
|
115
|
+
- oryx.markdown
|
34
116
|
extensions: []
|
35
117
|
extra_rdoc_files: []
|
36
118
|
files:
|
37
119
|
- .gitignore
|
120
|
+
- .travis.yml
|
38
121
|
- Gemfile
|
39
122
|
- LICENSE.txt
|
40
123
|
- README.md
|
41
124
|
- Rakefile
|
125
|
+
- bin/oryx
|
126
|
+
- bin/oryx.markdown
|
42
127
|
- lib/oryx.rb
|
128
|
+
- lib/oryx/lexer.rb
|
129
|
+
- lib/oryx/options.rb
|
130
|
+
- lib/oryx/preprocessor.rb
|
131
|
+
- lib/oryx/runner.rb
|
43
132
|
- lib/oryx/version.rb
|
44
133
|
- oryx.gemspec
|
45
|
-
|
134
|
+
- test/lib/oryx/lexer_test.rb
|
135
|
+
- test/lib/oryx/preprocessor_test.rb
|
136
|
+
- test/lib/oryx/version_test.rb
|
137
|
+
- test/test_helper.rb
|
138
|
+
- man/man1/oryx.1
|
139
|
+
homepage: http://github.com/rampantmonkey/oryx
|
46
140
|
licenses: []
|
47
141
|
post_install_message:
|
48
142
|
rdoc_options: []
|
@@ -54,16 +148,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
148
|
- - ! '>='
|
55
149
|
- !ruby/object:Gem::Version
|
56
150
|
version: '0'
|
151
|
+
segments:
|
152
|
+
- 0
|
153
|
+
hash: 620521509045120590
|
57
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
155
|
none: false
|
59
156
|
requirements:
|
60
157
|
- - ! '>='
|
61
158
|
- !ruby/object:Gem::Version
|
62
159
|
version: '0'
|
160
|
+
segments:
|
161
|
+
- 0
|
162
|
+
hash: 620521509045120590
|
63
163
|
requirements: []
|
64
164
|
rubyforge_project:
|
65
165
|
rubygems_version: 1.8.23
|
66
166
|
signing_key:
|
67
167
|
specification_version: 3
|
68
|
-
summary:
|
69
|
-
test_files:
|
168
|
+
summary: Take a program written in C-Flat and convert it to x86 assembly
|
169
|
+
test_files:
|
170
|
+
- test/lib/oryx/lexer_test.rb
|
171
|
+
- test/lib/oryx/preprocessor_test.rb
|
172
|
+
- test/lib/oryx/version_test.rb
|
173
|
+
- test/test_helper.rb
|