fancy 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +38 -86
- data/bin/fdoc +2 -22
- data/bin/fspec +8 -3
- data/bin/ifancy +1 -1
- data/boot/fancy_ext.rb +1 -0
- data/boot/fancy_ext/array.rb +19 -0
- data/boot/fancy_ext/class.rb +2 -4
- data/boot/fancy_ext/module.rb +2 -0
- data/boot/fancy_ext/object.rb +0 -17
- data/boot/rbx-compiler/compiler/ast/method_def.rb +0 -4
- data/boot/rbx-compiler/compiler/ast/singleton_method_def.rb +0 -7
- data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/fancy_parser.c +1 -0
- data/doc/api/fancy.css +10 -1
- data/doc/api/fancy.jsonp +1 -1
- data/doc/api/fdoc.js +22 -9
- data/doc/api/octocat.png +0 -0
- data/doc/features.md +1 -2
- data/examples/actors.fy +1 -2
- data/examples/armstrong_numbers.fy +7 -3
- data/examples/blocks.fy +3 -3
- data/examples/distributing_proxy.fy +31 -0
- data/examples/future_sends.fy +15 -0
- data/examples/person.fy +1 -2
- data/lib/argv.fy +1 -7
- data/lib/array.fy +7 -11
- data/lib/block.fy +15 -0
- data/lib/boot.fy +4 -3
- data/lib/class.fy +354 -10
- data/lib/compiler.fy +1 -1
- data/lib/compiler/ast/assign.fy +4 -8
- data/lib/compiler/ast/async_send.fy +1 -2
- data/lib/compiler/ast/block.fy +5 -0
- data/lib/compiler/ast/class_def.fy +2 -1
- data/lib/compiler/ast/expression_list.fy +1 -2
- data/lib/compiler/ast/future_send.fy +1 -2
- data/lib/compiler/ast/identifier.fy +34 -17
- data/lib/compiler/ast/literals.fy +31 -19
- data/lib/compiler/ast/match.fy +5 -4
- data/lib/compiler/ast/message_send.fy +3 -5
- data/lib/compiler/ast/method_def.fy +0 -3
- data/lib/compiler/ast/range.fy +2 -4
- data/lib/compiler/ast/return.fy +2 -4
- data/lib/compiler/ast/script.fy +2 -4
- data/lib/compiler/ast/singleton_method_def.fy +0 -3
- data/lib/compiler/ast/string_interpolation.fy +2 -2
- data/lib/compiler/ast/super.fy +2 -4
- data/lib/compiler/ast/try_catch.fy +13 -9
- data/lib/compiler/ast/tuple_literal.fy +1 -2
- data/lib/compiler/compiler.fy +2 -2
- data/lib/compiler/stages.fy +3 -6
- data/lib/contracts.fy +89 -57
- data/lib/dynamic_slot_object.fy +21 -3
- data/lib/enumerable.fy +140 -4
- data/lib/enumerator.fy +1 -1
- data/lib/eval.fy +23 -9
- data/lib/exception.fy +16 -0
- data/lib/false_class.fy +36 -5
- data/lib/fancy_spec.fy +64 -34
- data/lib/fdoc.fy +85 -24
- data/lib/file.fy +19 -0
- data/lib/future.fy +4 -46
- data/lib/hash.fy +113 -0
- data/lib/integer.fy +25 -6
- data/lib/iteration.fy +3 -3
- data/lib/main.fy +5 -0
- data/lib/matchers.fy +79 -0
- data/lib/nil_class.fy +8 -0
- data/lib/object.fy +109 -18
- data/lib/option_parser.fy +118 -0
- data/lib/package/dependency.fy +4 -8
- data/lib/package/dependency_installer.fy +1 -1
- data/lib/package/handler.fy +6 -0
- data/lib/package/installer.fy +43 -16
- data/lib/package/list.fy +1 -2
- data/lib/package/specification.fy +5 -5
- data/lib/package/uninstaller.fy +9 -2
- data/lib/parser.fy +1 -3
- data/lib/parser/ext/ext.c +1 -0
- data/lib/parser/ext/lexer.lex +5 -0
- data/lib/parser/methods.fy +48 -46
- data/lib/proxies.fy +151 -0
- data/lib/rbx.fy +1 -0
- data/lib/rbx/actor.fy +16 -18
- data/lib/rbx/array.fy +18 -3
- data/lib/rbx/block.fy +1 -7
- data/lib/rbx/class.fy +54 -9
- data/lib/rbx/code_loader.fy +2 -5
- data/lib/rbx/compiled_method.fy +31 -0
- data/lib/rbx/debugger.fy +66 -0
- data/lib/rbx/directory.fy +8 -3
- data/lib/rbx/documentation.fy +1 -1
- data/lib/rbx/file.fy +22 -0
- data/lib/rbx/integer.fy +1 -1
- data/lib/rbx/match_data.fy +2 -1
- data/lib/rbx/method.fy +26 -0
- data/lib/rbx/object.fy +8 -3
- data/lib/rbx/regexp.fy +6 -3
- data/lib/rbx/string.fy +9 -1
- data/lib/rbx/stringio.fy +12 -0
- data/lib/rbx/symbol.fy +4 -0
- data/lib/stack.fy +1 -1
- data/lib/string.fy +34 -0
- data/lib/stringio.fy +1 -1
- data/lib/symbol.fy +6 -2
- data/lib/system.fy +15 -1
- data/lib/tuple.fy +5 -2
- data/lib/version.fy +1 -1
- data/ruby_lib/fdoc +2 -22
- data/tests/array.fy +3 -17
- data/tests/class.fy +312 -10
- data/tests/contracts.fy +51 -0
- data/tests/distributing_proxy.fy +28 -0
- data/tests/enumerable.fy +104 -1
- data/tests/exception.fy +35 -0
- data/tests/fixnum.fy +1 -1
- data/tests/hash.fy +81 -1
- data/tests/integer.fy +9 -0
- data/tests/matchers.fy +18 -0
- data/tests/method.fy +8 -14
- data/tests/object.fy +76 -2
- data/tests/option_parser.fy +80 -0
- data/tests/string.fy +21 -0
- data/tests/stringio.fy +1 -1
- data/tests/tuple.fy +1 -1
- metadata +21 -44
- data/examples/arithmetic.fy +0 -7
- data/examples/array.fy +0 -50
- data/examples/boolean.fy +0 -24
- data/examples/class.fy +0 -68
- data/examples/constant_access.fy +0 -15
- data/examples/default_args.fy +0 -20
- data/examples/define_methods.fy +0 -15
- data/examples/dynamic_output.fy +0 -15
- data/examples/empty_catch.fy +0 -4
- data/examples/exception.fy +0 -9
- data/examples/files.fy +0 -23
- data/examples/finally.fy +0 -5
- data/examples/future.fy +0 -30
- data/examples/future_composition.fy +0 -20
- data/examples/futures.fy +0 -9
- data/examples/game_of_life.fy +0 -148
- data/examples/html_generator.fy +0 -84
- data/examples/implicit_return.fy +0 -3
- data/examples/matchers.fy +0 -6
- data/examples/nested_try.fy +0 -9
- data/examples/numbers.fy +0 -12
- data/examples/rbx/and_or.fy +0 -7
- data/examples/rbx/blocks.fy +0 -22
- data/examples/rbx/classes.fy +0 -32
- data/examples/rbx/hello.fy +0 -8
- data/examples/rbx/include.fy +0 -12
- data/examples/rbx/inherit.fy +0 -11
- data/examples/rbx/methods.fy +0 -15
- data/examples/rbx/nested_classes.fy +0 -9
- data/examples/rbx/require.fy +0 -3
- data/examples/rbx/strings.fy +0 -5
- data/examples/require.fy +0 -7
- data/examples/return.fy +0 -13
- data/examples/singleton_methods.fy +0 -21
- data/examples/threads.fy +0 -18
- data/examples/tuple.fy +0 -8
- data/examples/webserver/webserver.fy +0 -15
- data/lib/proxy.fy +0 -86
- data/lib/thread_pool.fy +0 -102
@@ -0,0 +1,118 @@
|
|
1
|
+
class OptionParser {
|
2
|
+
"""
|
3
|
+
Parses command-line options from a given @Array@ (usually @ARGV) and
|
4
|
+
executes registered handlers for options specified.
|
5
|
+
"""
|
6
|
+
|
7
|
+
class Option {
|
8
|
+
read_slots: ('option, 'arg, 'doc_string, 'block)
|
9
|
+
def initialize: @option arg: @arg doc: @doc_string block: @block
|
10
|
+
def name_with_arg {
|
11
|
+
if: @arg then: {
|
12
|
+
"#{@option} [#{@arg}]"
|
13
|
+
} else: {
|
14
|
+
@option
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
class InvalidOptionsError : StandardError
|
20
|
+
|
21
|
+
read_slots: ('options, 'parsed_options)
|
22
|
+
read_write_slots: ('banner, 'exit_on_help, 'remove_after_parsed)
|
23
|
+
|
24
|
+
def initialize: @block (nil) {
|
25
|
+
"""
|
26
|
+
@block (Optional) @Block@ to be called with @self for easy setup.
|
27
|
+
|
28
|
+
Creates a new OptionParser.
|
29
|
+
|
30
|
+
Example:
|
31
|
+
o = OptionParser new: @{
|
32
|
+
with: \"--my-option\" doc: \"Sets some option value\" do: {
|
33
|
+
# do stuff in here...
|
34
|
+
}
|
35
|
+
}
|
36
|
+
o parse: ARGV # parse options from ARGV
|
37
|
+
"""
|
38
|
+
|
39
|
+
@options = <[]>
|
40
|
+
@parsed_options = <[]>
|
41
|
+
@banner = nil
|
42
|
+
@exit_on_help = true
|
43
|
+
@remove_after_parsed = false
|
44
|
+
|
45
|
+
with: "--help" doc: "Display this information" do: {
|
46
|
+
print_help_info
|
47
|
+
{ System exit } if: @exit_on_help
|
48
|
+
}
|
49
|
+
|
50
|
+
{ @block call: [self] } if: @block
|
51
|
+
}
|
52
|
+
|
53
|
+
def with: option_string doc: doc_string do: block {
|
54
|
+
"""
|
55
|
+
@option_string Option flag and (optional) argument within \"[]\", e.g. \"--file [filename]\".
|
56
|
+
@doc_string Documentation @String@ for @option_string that is used in the standard @--help option.
|
57
|
+
@block @Block@ to be executed if @option_string is matched during parsing. If the option takes an argument it will be passed to @block as an argument.
|
58
|
+
|
59
|
+
Example:
|
60
|
+
o = OptionParser new
|
61
|
+
o with: \"--file [filename]\" doc: \"Use this file for processing\" do: |filename| {
|
62
|
+
# do something with filename
|
63
|
+
}
|
64
|
+
"""
|
65
|
+
|
66
|
+
option, arg = option_string split: " "
|
67
|
+
if: arg then: {
|
68
|
+
match arg {
|
69
|
+
case /\[(.+)\]/ -> |_, arg_name|
|
70
|
+
arg = arg_name downcase
|
71
|
+
case _ -> InvalidOptionsError new: "Could not correctly parse option argument: #{arg}" . raise!
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
o = Option new: option arg: arg doc: doc_string block: block
|
76
|
+
@options[option]: o
|
77
|
+
}
|
78
|
+
|
79
|
+
def parse: args {
|
80
|
+
"""
|
81
|
+
@args @Array@ of arguments to parse options from. Typically you'd pass @ARGV here.
|
82
|
+
|
83
|
+
Parses options from @args and executes registered option handlers.
|
84
|
+
"""
|
85
|
+
|
86
|
+
@options each: |name opt| {
|
87
|
+
if: (args index: name) then: |idx| {
|
88
|
+
block = opt block
|
89
|
+
match block arity {
|
90
|
+
case 0 -> block call
|
91
|
+
case 1 ->
|
92
|
+
arg = args at: (idx + 1)
|
93
|
+
block call: [arg]
|
94
|
+
@parsed_options[name]: arg
|
95
|
+
{ args remove_at: idx } if: @remove_after_parsed
|
96
|
+
}
|
97
|
+
|
98
|
+
{ args remove_at: idx } if: @remove_after_parsed
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
def print_help_info {
|
104
|
+
"""
|
105
|
+
Displays the @--help information on @*stdout* based on all options that were registered via @OptionParser#with:doc:do:@.
|
106
|
+
"""
|
107
|
+
|
108
|
+
max_size = @options map: |name opt| { opt name_with_arg size } . max
|
109
|
+
if: @banner then: {
|
110
|
+
*stdout* println: @banner
|
111
|
+
*stdout* println
|
112
|
+
}
|
113
|
+
@options keys sort each: |name| {
|
114
|
+
opt = @options[name]
|
115
|
+
*stdout* printf(" %-#{max_size}s %s\n", opt name_with_arg, opt doc_string)
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
data/lib/package/dependency.fy
CHANGED
@@ -5,10 +5,8 @@ class Fancy Package {
|
|
5
5
|
Represents a Dependency to another Package with a given version.
|
6
6
|
"""
|
7
7
|
|
8
|
-
read_slots:
|
9
|
-
|
10
|
-
def initialize: @name version: @version ('latest) {
|
11
|
-
}
|
8
|
+
read_slots: ('name, 'version)
|
9
|
+
def initialize: @name version: @version ('latest);
|
12
10
|
}
|
13
11
|
|
14
12
|
class RubyDependency {
|
@@ -16,10 +14,8 @@ class Fancy Package {
|
|
16
14
|
Same as @Fancy::Package::Dependency@, just for rubygem packages.
|
17
15
|
"""
|
18
16
|
|
19
|
-
read_slots:
|
20
|
-
|
21
|
-
def initialize: @gem_name version: @version ('latest) {
|
22
|
-
}
|
17
|
+
read_slots: ('gem_name, 'version)
|
18
|
+
def initialize: @gem_name version: @version ('latest);
|
23
19
|
|
24
20
|
def install {
|
25
21
|
"""
|
@@ -16,7 +16,7 @@ class Fancy {
|
|
16
16
|
"Installing dependency: #{dep name} (#{dep version})" println
|
17
17
|
Fancy Package install: (dep name) version: (dep version)
|
18
18
|
}
|
19
|
-
s
|
19
|
+
s ruby_dependencies each: |dep| {
|
20
20
|
"Installing Ruby dependency: #{dep gem_name} (#{dep version})" println
|
21
21
|
dep install
|
22
22
|
}
|
data/lib/package/handler.fy
CHANGED
data/lib/package/installer.fy
CHANGED
@@ -12,8 +12,8 @@ class Fancy Package {
|
|
12
12
|
|
13
13
|
def initialize: @package_name version: @version ('latest) install_path: @install_path (ENV["FANCY_PACKAGE_DIR"]) {
|
14
14
|
"""
|
15
|
-
Creates a new @Package
|
16
|
-
optional version (default is 'latest) and an optional
|
15
|
+
Creates a new @Fancy::Package@ installer for a given package name, an
|
16
|
+
optional version (default is @'latest) and an optional
|
17
17
|
installation path (default is the standard installation path for
|
18
18
|
Fancy packages).
|
19
19
|
"""
|
@@ -61,26 +61,40 @@ class Fancy Package {
|
|
61
61
|
}
|
62
62
|
|
63
63
|
def latest_tag {
|
64
|
-
"
|
64
|
+
"""
|
65
|
+
Returns the latest tag (sorted alphabetically).
|
66
|
+
"""
|
65
67
|
|
66
|
-
tags
|
67
|
-
if: (tags size > 0) then: {
|
68
|
-
tags keys sort last
|
69
|
-
}
|
68
|
+
tags sort last
|
70
69
|
}
|
71
70
|
|
72
71
|
def tags {
|
73
|
-
"
|
72
|
+
"""
|
73
|
+
@return @Array@ of git tags in the package's Github repository.
|
74
|
+
"""
|
74
75
|
|
75
|
-
require("yaml")
|
76
76
|
require("open-uri")
|
77
|
+
require("rubygems")
|
78
|
+
require("json")
|
77
79
|
|
78
|
-
url = "
|
79
|
-
|
80
|
+
url = "https://api.github.com/repos/#{@package_name}/git/refs/tags"
|
81
|
+
|
82
|
+
try {
|
83
|
+
return JSON load(open(url)) map: |tag| {
|
84
|
+
tag["ref"] split: "refs/tags/" . last
|
85
|
+
}
|
86
|
+
} catch OpenURI HTTPError {
|
87
|
+
return [] # no tags available, default to master (latest)
|
88
|
+
}
|
80
89
|
}
|
81
90
|
|
82
91
|
def has_version?: version {
|
83
|
-
"
|
92
|
+
"""
|
93
|
+
@version Version of package to check for.
|
94
|
+
@return @true, if this package has the given version, @false otherwise.
|
95
|
+
|
96
|
+
Indicates, if a given version for this package is available on Github.
|
97
|
+
"""
|
84
98
|
|
85
99
|
match version {
|
86
100
|
case "master" -> true
|
@@ -122,6 +136,12 @@ class Fancy Package {
|
|
122
136
|
}
|
123
137
|
|
124
138
|
def unpack_file: filename {
|
139
|
+
"""
|
140
|
+
@filename File name of package's downloaded .tar.gz file (from Github) to extract
|
141
|
+
|
142
|
+
Unpacks the given @filename and installs it into Fancy's package install dir.
|
143
|
+
"""
|
144
|
+
|
125
145
|
"Unpacking " ++ filename println
|
126
146
|
System do: $ ["tar xf ", @download_path, "/", filename, " -C ", @install_path, "/"] join
|
127
147
|
output = System pipe: $ ["tar tf ", @download_path, "/", filename] join
|
@@ -139,12 +159,19 @@ class Fancy Package {
|
|
139
159
|
}
|
140
160
|
|
141
161
|
def fulfill_spec: spec {
|
162
|
+
"""
|
163
|
+
@spec @Fancy::Package::Specification@ to be fulfilled.
|
164
|
+
|
165
|
+
Installs all dependencies of @spec, sets up symlinks for binary files in @spec,
|
166
|
+
as well as installing the include-file into the Fancy package lib dir.
|
167
|
+
"""
|
168
|
+
|
142
169
|
unless: (spec include_files empty?) do: {
|
143
170
|
File open: (lib_path + "/" + (spec package_name)) modes: ['write] with: |f| {
|
144
171
|
spec include_files each: |if| {
|
145
|
-
unless: (spec
|
172
|
+
unless: (spec ruby_dependencies empty?) do: {
|
146
173
|
f writeln: "require(\"rubygems\")"
|
147
|
-
spec
|
174
|
+
spec ruby_dependencies each: |rd| {
|
148
175
|
f writeln: "require(\"#{rd gem_name}\")"
|
149
176
|
}
|
150
177
|
}
|
@@ -167,10 +194,10 @@ class Fancy Package {
|
|
167
194
|
}
|
168
195
|
|
169
196
|
spec dependencies each: |dep| {
|
170
|
-
Package install: dep
|
197
|
+
Fancy Package install: (dep name) version: (dep version)
|
171
198
|
}
|
172
199
|
|
173
|
-
spec
|
200
|
+
spec ruby_dependencies each: |dep| { dep install }
|
174
201
|
}
|
175
202
|
}
|
176
203
|
}
|
data/lib/package/list.fy
CHANGED
@@ -3,13 +3,13 @@ class Fancy Package {
|
|
3
3
|
@@specs = <[]>
|
4
4
|
|
5
5
|
read_write_slots: ['author, 'email, 'include_files, 'bin_files,
|
6
|
-
'description, 'homepage, 'version, 'gh_user]
|
6
|
+
'description, 'homepage, 'version, 'gh_user, 'package_name]
|
7
7
|
|
8
|
-
read_slots: ['
|
8
|
+
read_slots: ['dependencies, 'ruby_dependencies]
|
9
9
|
|
10
10
|
def initialize: @package_name with: block {
|
11
11
|
@dependencies = []
|
12
|
-
@
|
12
|
+
@ruby_dependencies = []
|
13
13
|
@include_files = []
|
14
14
|
@bin_files = []
|
15
15
|
|
@@ -34,7 +34,7 @@ class Fancy Package {
|
|
34
34
|
version = d second
|
35
35
|
{ version = 'latest } unless: version
|
36
36
|
dep = RubyDependency new: gem_name version: version
|
37
|
-
@
|
37
|
+
@ruby_dependencies << dep
|
38
38
|
}
|
39
39
|
}
|
40
40
|
|
@@ -45,7 +45,7 @@ class Fancy Package {
|
|
45
45
|
|
46
46
|
def add_ruby_dependency: gem_name version: version ('latest) {
|
47
47
|
dep = RubyDependency new: gem_name version: version
|
48
|
-
@
|
48
|
+
@ruby_dependencies << dep
|
49
49
|
}
|
50
50
|
|
51
51
|
def to_s {
|
data/lib/package/uninstaller.fy
CHANGED
@@ -9,6 +9,7 @@ class Fancy Package {
|
|
9
9
|
Specification delete_specification: spec from: $ Fancy Package package_list_file
|
10
10
|
delete_package_dir
|
11
11
|
delete_lib_file: (spec package_name)
|
12
|
+
delete_bin_files: (installed_bin_symlinks: spec)
|
12
13
|
"Successfully uninstalled package #{spec package_name} with version: #{spec version}." println
|
13
14
|
} else: {
|
14
15
|
System abort: "No package found for #{@package_name} with version '#{@version}'."
|
@@ -16,7 +17,7 @@ class Fancy Package {
|
|
16
17
|
}
|
17
18
|
|
18
19
|
def delete_package_dir {
|
19
|
-
require("
|
20
|
+
require("fileutils")
|
20
21
|
if: (Directory exists?: installed_path) then: {
|
21
22
|
"Deleting directory: #{installed_path}" println
|
22
23
|
FileUtils rm_rf(installed_path)
|
@@ -26,7 +27,13 @@ class Fancy Package {
|
|
26
27
|
def delete_lib_file: package_name {
|
27
28
|
lib_file = "#{lib_path}/#{package_name}"
|
28
29
|
"Deleting: #{lib_file}" println
|
29
|
-
File delete
|
30
|
+
File delete!: lib_file
|
31
|
+
}
|
32
|
+
|
33
|
+
def delete_bin_files: bin_files {
|
34
|
+
bin_files each: |bf| {
|
35
|
+
File delete!: bf
|
36
|
+
}
|
30
37
|
}
|
31
38
|
}
|
32
39
|
}
|
data/lib/parser.fy
CHANGED
data/lib/parser/ext/ext.c
CHANGED
data/lib/parser/ext/lexer.lex
CHANGED
@@ -54,6 +54,7 @@ identifier @?@?({lower}|[_&*])({letter}|{digit}|{special_under})*
|
|
54
54
|
selector ({letter}|[_&*])({letter}|{digit}|{special_under})*":"
|
55
55
|
constant {capital}({letter}|{digit}|{special_under})*
|
56
56
|
nested_constant ({constant}::)+{constant}
|
57
|
+
toplevel_constant ::({constant}|{nested_constant})
|
57
58
|
symbol_lit \'({identifier}|{operator}|{constant}|:|"[]"|"|"|".")+
|
58
59
|
ruby_send_open ({constant}|{identifier}){lparen}
|
59
60
|
ruby_oper_open {operator}{lparen}
|
@@ -166,6 +167,10 @@ escaped_newline "\\".*\n
|
|
166
167
|
yylval.object = rb_str_new2(yytext);
|
167
168
|
return CONSTANT;
|
168
169
|
}
|
170
|
+
{toplevel_constant} {
|
171
|
+
yylval.object = rb_str_new2(yytext);
|
172
|
+
return CONSTANT;
|
173
|
+
}
|
169
174
|
{symbol_lit} {
|
170
175
|
yylval.object = rb_str_new2(yytext);
|
171
176
|
return SYMBOL_LITERAL;
|
data/lib/parser/methods.fy
CHANGED
@@ -2,8 +2,10 @@ require: "parse_error"
|
|
2
2
|
|
3
3
|
class Fancy {
|
4
4
|
class Parser {
|
5
|
-
SelectorVarDefault = Struct
|
5
|
+
SelectorVarDefault = Struct new('selector, 'variable, 'default)
|
6
|
+
SelectorVarDefault read_write_slots: ('selector, 'variable, 'default)
|
6
7
|
SelectorValue = Struct new('selector, 'value)
|
8
|
+
SelectorValue read_write_slots: ('selector, 'value)
|
7
9
|
|
8
10
|
def self parse_file: filename line: line (1) {
|
9
11
|
new: filename line: line . parse_file . script
|
@@ -17,9 +19,11 @@ class Fancy {
|
|
17
19
|
ParseError new: line message: text filename: (Thread current['__fancy__parser__filename__]) . raise!
|
18
20
|
}
|
19
21
|
|
20
|
-
read_write_slots:
|
22
|
+
read_write_slots: ('filename, 'line, 'script)
|
21
23
|
|
22
|
-
def initialize: @filename line: @line {
|
24
|
+
def initialize: @filename line: @line {
|
25
|
+
Thread current['__fancy__parser__filename__]: @filename
|
26
|
+
}
|
23
27
|
|
24
28
|
def body: body {
|
25
29
|
@script = AST Script new: @line file: @filename body: body
|
@@ -33,8 +37,8 @@ class Fancy {
|
|
33
37
|
def ast: line identity: identity { identity }
|
34
38
|
|
35
39
|
def ast: line concat: object into: ary ([]) {
|
36
|
-
if: (object
|
37
|
-
ary
|
40
|
+
if: (object is_a?: Array) then: {
|
41
|
+
ary append: object
|
38
42
|
} else: {
|
39
43
|
{ ary << object } if: object
|
40
44
|
}
|
@@ -49,16 +53,16 @@ class Fancy {
|
|
49
53
|
}
|
50
54
|
|
51
55
|
def ast: line fixnum: text base: base (10) {
|
52
|
-
AST FixnumLiteral new: line value:
|
56
|
+
AST FixnumLiteral new: line value: $ text to_i: base
|
53
57
|
}
|
54
58
|
|
55
59
|
def ast: line number: text base: base (10) {
|
56
|
-
AST NumberLiteral new: line value:
|
60
|
+
AST NumberLiteral new: line value: $ text to_f
|
57
61
|
}
|
58
62
|
|
59
63
|
def ast: line symbol: text {
|
60
64
|
str = text from: 1 to: -1
|
61
|
-
AST SymbolLiteral new: line value:
|
65
|
+
AST SymbolLiteral new: line value: $ str to_sym
|
62
66
|
}
|
63
67
|
|
64
68
|
def ast: line regexp: text {
|
@@ -68,41 +72,34 @@ class Fancy {
|
|
68
72
|
|
69
73
|
def ast: line string: text {
|
70
74
|
str = text from: 1 to: -2
|
71
|
-
match str
|
72
|
-
# OK, I know this is ugly. But it works for now, so let's just go with it.
|
73
|
-
# TODO: Clean this up or make it simpler...
|
74
|
-
|
75
|
-
# this case handles string interpolation
|
76
|
-
case /(.*)#{(.*)}(.*)/m -> |matches|
|
77
|
-
prefix = matches[1]
|
78
|
-
interpol_str = matches[2]
|
79
|
-
suffix = matches[3]
|
80
|
-
|
81
|
-
prefix_str = ast: line string: (" " + prefix + " ") # hack, pre- & append " " since it gets removed
|
82
|
-
suffix_str = ast: line string: (" " + suffix + " ")
|
83
|
-
interpol_ast = AST StringInterpolation new: line code: interpol_str
|
84
|
-
# create messagesend to concatenate:
|
85
|
-
concat_ident = ast: line identifier: "<<"
|
86
|
-
interpol_send = AST MessageSend new: line message: concat_ident to: prefix_str args: (AST MessageArgs new: line args: [interpol_ast])
|
87
|
-
|
88
|
-
# don't concatenate suffix if it's empty..
|
89
|
-
unless: (suffix == "") do: {
|
90
|
-
interpol_send = AST MessageSend new: line message: concat_ident to: interpol_send args: (AST MessageArgs new: line args: [suffix_str])
|
91
|
-
}
|
75
|
+
match_data = /#{([^}]*)}/ match: str
|
92
76
|
|
93
|
-
|
94
|
-
|
95
|
-
|
77
|
+
{ return AST StringLiteral new: line value: str } unless: match_data
|
78
|
+
|
79
|
+
# this case handles string interpolation
|
80
|
+
prefix_str = ast: line string: $ " " + (match_data pre_match) + " " # hack, pre- & append " " since it gets removed
|
81
|
+
suffix_str = ast: line string: $ " " + (match_data post_match) + " "
|
82
|
+
interpol_ast = AST StringInterpolation new: line code: (match_data[1]) filename: @filename
|
83
|
+
|
84
|
+
# create messagesend to concatenate:
|
85
|
+
concat_ident = ast: line identifier: "<<"
|
86
|
+
interpol_send = AST MessageSend new: line message: concat_ident to: prefix_str args: $ AST MessageArgs new: line args: [interpol_ast]
|
87
|
+
|
88
|
+
# don't concatenate suffix if it's empty..
|
89
|
+
unless: (match_data post_match == "") do: {
|
90
|
+
interpol_send = AST MessageSend new: line message: concat_ident to: interpol_send args: $ AST MessageArgs new: line args: [suffix_str]
|
96
91
|
}
|
92
|
+
|
93
|
+
interpol_send
|
97
94
|
}
|
98
95
|
|
99
96
|
def ast: line multi_line_string: string {
|
100
|
-
ast: line string:
|
97
|
+
ast: line string: $ string from: 2 to: -3
|
101
98
|
}
|
102
99
|
|
103
100
|
def ast: line backtick: backtick_string {
|
104
101
|
str = ast: line string: backtick_string
|
105
|
-
selector =
|
102
|
+
selector = ast: line identifier: "backtick:"
|
106
103
|
args = AST MessageArgs new: line args: [str]
|
107
104
|
AST MessageSend new: line message: selector to: (AST Self new: line) args: args
|
108
105
|
}
|
@@ -194,13 +191,13 @@ class Fancy {
|
|
194
191
|
AST MessageArgs new: line args: []
|
195
192
|
}
|
196
193
|
name = message
|
197
|
-
if: (message
|
194
|
+
if: (message is_a?: String) then: {
|
198
195
|
name = AST Identifier from: message line: line
|
199
196
|
}
|
200
|
-
if: (message
|
201
|
-
name = message map: |m| { m selector
|
197
|
+
if: (message is_a?: Array) then: {
|
198
|
+
name = message map: |m| { m selector string } . join
|
202
199
|
name = AST Identifier new: line string: name
|
203
|
-
args = message map: |m| { m value
|
200
|
+
args = message map: |m| { m value }
|
204
201
|
args = AST MessageArgs new: line args: args
|
205
202
|
}
|
206
203
|
AST MessageSend new: line message: name to: receiver args: args
|
@@ -217,13 +214,13 @@ class Fancy {
|
|
217
214
|
}
|
218
215
|
|
219
216
|
def method_name: margs {
|
220
|
-
margs map: |a| { a selector
|
217
|
+
margs map: |a| { a selector string } . join("")
|
221
218
|
}
|
222
219
|
|
223
220
|
def method: margs delegators: block {
|
224
|
-
idx = margs index
|
221
|
+
idx = margs index: |m| { m default != nil }
|
225
222
|
if: idx then: {
|
226
|
-
line = margs first selector
|
223
|
+
line = margs first selector line
|
227
224
|
target = method_name: margs
|
228
225
|
(margs size - idx) times: |pos| {
|
229
226
|
required = margs from: 0 to: (idx + pos - 1)
|
@@ -232,7 +229,7 @@ class Fancy {
|
|
232
229
|
if: only_default_args then: {
|
233
230
|
required = []
|
234
231
|
}
|
235
|
-
params = required map: |r| { r variable
|
232
|
+
params = required map: |r| { r variable } . + $ default map: |d| { d default }
|
236
233
|
|
237
234
|
forward = AST MessageSend new: line \
|
238
235
|
message: (AST Identifier from: target line: line) \
|
@@ -245,7 +242,7 @@ class Fancy {
|
|
245
242
|
# use base method name (e.g. "foo:" -> "foo") for the method to be generated
|
246
243
|
# if there are no more arguments left (only default args left)
|
247
244
|
if: only_default_args then: {
|
248
|
-
required = AST Identifier from: (margs first selector
|
245
|
+
required = AST Identifier from: (margs first selector string from: 0 to: -2) line: line
|
249
246
|
}
|
250
247
|
|
251
248
|
block call: [required, body]
|
@@ -275,7 +272,7 @@ class Fancy {
|
|
275
272
|
} else: {
|
276
273
|
name = method_name: margs
|
277
274
|
name = AST Identifier new: line string: name
|
278
|
-
args = margs map
|
275
|
+
args = margs map: |m| { m variable string }
|
279
276
|
args = AST MethodArgs new: line args: args
|
280
277
|
if: owner then: {
|
281
278
|
AST SingletonMethodDef new: line name: name args: args \
|
@@ -323,12 +320,17 @@ class Fancy {
|
|
323
320
|
AST MatchClause new: line expr: expr body: body args: match_args
|
324
321
|
}
|
325
322
|
|
326
|
-
def ast: line ex_handler: expr_list cond: cond (AST Identifier from: "
|
323
|
+
def ast: line ex_handler: expr_list cond: cond (AST Identifier from: "StandardError" line: line) var: var (nil) {
|
327
324
|
AST ExceptionHandler new: line condition: cond var: var body: expr_list
|
328
325
|
}
|
329
326
|
|
330
327
|
def ast: line try_block: body ex_handlers: handlers finally_block: finaly (AST NilLiteral new: line) {
|
331
|
-
|
328
|
+
match finaly {
|
329
|
+
case AST NilLiteral -> AST TryCatch new: line body: body handlers: handlers ensure: finaly
|
330
|
+
case _ ->
|
331
|
+
inner = AST TryCatch new: line body: body handlers: handlers ensure: (AST NilLiteral new: line)
|
332
|
+
AST TryCatch new: line body: (AST ExpressionList new: line list: [inner]) handlers: [] ensure: finaly
|
333
|
+
}
|
332
334
|
}
|
333
335
|
|
334
336
|
def ast: line ruby_send: text {
|