fancy 0.7.0 → 0.8.0
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.
- 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 {
|