peruby 0.1.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.
- checksums.yaml +7 -0
- data/.rubocop.yml +35 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +92 -0
- data/bin/peruby +9 -0
- data/doc/COMPAT.md +54 -0
- data/doc/CONTRIBUTING.md +21 -0
- data/doc/DESIGN.md +25 -0
- data/doc/INCOMPATIBILITIES.md +30 -0
- data/doc/PERF.md +51 -0
- data/doc/ROADMAP.md +18 -0
- data/examples/hello.pl +1 -0
- data/examples/json.pl +2 -0
- data/examples/object.pl +5 -0
- data/examples/word_count.pl +6 -0
- data/lib/peruby/cli.rb +224 -0
- data/lib/peruby/compile_unit.rb +103 -0
- data/lib/peruby/compiler.rb +224 -0
- data/lib/peruby/errors.rb +40 -0
- data/lib/peruby/lexer/heredoc.rb +8 -0
- data/lib/peruby/lexer/keywords.rb +63 -0
- data/lib/peruby/lexer/number.rb +32 -0
- data/lib/peruby/lexer/quote_like.rb +72 -0
- data/lib/peruby/lexer/source_scanner.rb +104 -0
- data/lib/peruby/lexer/state.rb +46 -0
- data/lib/peruby/lexer/structure_scanner.rb +149 -0
- data/lib/peruby/lexer/term_scanner.rb +301 -0
- data/lib/peruby/lexer/token.rb +16 -0
- data/lib/peruby/lexer.rb +123 -0
- data/lib/peruby/node.rb +88 -0
- data/lib/peruby/op/assign.rb +128 -0
- data/lib/peruby/op/builtin.rb +903 -0
- data/lib/peruby/op/call.rb +378 -0
- data/lib/peruby/op/control.rb +256 -0
- data/lib/peruby/op/element.rb +136 -0
- data/lib/peruby/op/expression.rb +342 -0
- data/lib/peruby/op/io.rb +113 -0
- data/lib/peruby/op/list.rb +102 -0
- data/lib/peruby/op/literal.rb +84 -0
- data/lib/peruby/op/loop.rb +158 -0
- data/lib/peruby/op/regexp.rb +288 -0
- data/lib/peruby/op/variable.rb +534 -0
- data/lib/peruby/op.rb +47 -0
- data/lib/peruby/parser/grammar.rb +5797 -0
- data/lib/peruby/parser/grammar.y +576 -0
- data/lib/peruby/parser.rb +14 -0
- data/lib/peruby/runtime/code.rb +21 -0
- data/lib/peruby/runtime/conv.rb +140 -0
- data/lib/peruby/runtime/directory_handle.rb +18 -0
- data/lib/peruby/runtime/env.rb +129 -0
- data/lib/peruby/runtime/glob.rb +24 -0
- data/lib/peruby/runtime/interpolation.rb +223 -0
- data/lib/peruby/runtime/io_handle.rb +37 -0
- data/lib/peruby/runtime/local_stack.rb +90 -0
- data/lib/peruby/runtime/match_state.rb +62 -0
- data/lib/peruby/runtime/module_loader.rb +133 -0
- data/lib/peruby/runtime/mro.rb +94 -0
- data/lib/peruby/runtime/perl_array.rb +81 -0
- data/lib/peruby/runtime/perl_hash.rb +57 -0
- data/lib/peruby/runtime/ref.rb +43 -0
- data/lib/peruby/runtime/regexp_compiler.rb +75 -0
- data/lib/peruby/runtime/scalar.rb +27 -0
- data/lib/peruby/runtime/sprintf.rb +54 -0
- data/lib/peruby/runtime/stash.rb +50 -0
- data/lib/peruby/runtime/test_builder.rb +47 -0
- data/lib/peruby/runtime.rb +325 -0
- data/lib/peruby/validator.rb +236 -0
- data/lib/peruby/version.rb +5 -0
- data/lib/peruby.rb +31 -0
- data/t/00-basic.t +5 -0
- data/t/lib/MiniTest.pm +22 -0
- metadata +130 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Peruby
|
|
4
|
+
class Lexer
|
|
5
|
+
# One lexical token and its source location.
|
|
6
|
+
Token = Data.define(:type, :value, :file, :line, :column) do
|
|
7
|
+
def to_a
|
|
8
|
+
[type, value]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
"#{file}:#{line}:#{column} #{type} #{value.inspect}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/peruby/lexer.rb
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lexer/token'
|
|
4
|
+
require_relative 'lexer/state'
|
|
5
|
+
require_relative 'lexer/keywords'
|
|
6
|
+
require_relative 'lexer/number'
|
|
7
|
+
require_relative 'lexer/quote_like'
|
|
8
|
+
require_relative 'lexer/heredoc'
|
|
9
|
+
require_relative 'lexer/term_scanner'
|
|
10
|
+
require_relative 'lexer/structure_scanner'
|
|
11
|
+
require_relative 'lexer/source_scanner'
|
|
12
|
+
|
|
13
|
+
module Peruby
|
|
14
|
+
# Stateful Perl lexer. It resolves term/operator ambiguities without parser feedback.
|
|
15
|
+
class Lexer
|
|
16
|
+
OPERATORS = {
|
|
17
|
+
'**=' => :ASSIGNOP, '//=' => :ASSIGNOP, '&&=' => :ASSIGNOP, '||=' => :ASSIGNOP,
|
|
18
|
+
'<<=' => :ASSIGNOP, '>>=' => :ASSIGNOP, '&=' => :ASSIGNOP, '|=' => :ASSIGNOP, '^=' => :ASSIGNOP,
|
|
19
|
+
'...' => :DOTDOTDOT, '**' => :POW,
|
|
20
|
+
'//' => :DORDOR, '&&' => :ANDAND, '||' => :OROR, '=~' => :MATCHOP,
|
|
21
|
+
'!~' => :NOTMATCHOP, '==' => :EQ, '!=' => :NE, '<=' => :LE, '>=' => :GE,
|
|
22
|
+
'<=>' => :CMP, '->' => :ARROW, '=>' => :FATCOMMA, '..' => :DOTDOT,
|
|
23
|
+
'<<' => :LSHIFT, '>>' => :RSHIFT, '++' => :INC, '--' => :DEC,
|
|
24
|
+
'+=' => :ASSIGNOP, '-=' => :ASSIGNOP, '*=' => :ASSIGNOP, '/=' => :ASSIGNOP,
|
|
25
|
+
'%=' => :ASSIGNOP, '.=' => :ASSIGNOP, 'x=' => :ASSIGNOP,
|
|
26
|
+
'=' => :'=', '+' => :+, '-' => :-, '*' => :*, '/' => :/, '%' => :%, '.' => :'.',
|
|
27
|
+
'<' => :<, '>' => :>, '!' => :!, '~' => :~, '&' => :&, '|' => :|, '^' => :^,
|
|
28
|
+
'?' => :'?', ':' => :':', ',' => :',', ';' => :';', '\\' => :REFGEN
|
|
29
|
+
}.freeze
|
|
30
|
+
SORTED_OPERATORS = OPERATORS.keys.sort_by(&:length).reverse.freeze
|
|
31
|
+
SIGILS = { '$' => :SCALAR_VAR, '@' => :ARRAY_VAR, '%' => :HASH_VAR,
|
|
32
|
+
'&' => :CODE_VAR, '*' => :GLOB_VAR }.freeze
|
|
33
|
+
QUOTE_OPERATORS = %w[q qq qw qr m s tr y].freeze
|
|
34
|
+
FILETEST = /\A-[erwxofdszlpSbcugktTBAMC](?!\w)/
|
|
35
|
+
|
|
36
|
+
include TermScanner
|
|
37
|
+
include StructureScanner
|
|
38
|
+
include SourceScanner
|
|
39
|
+
|
|
40
|
+
attr_reader :state, :data
|
|
41
|
+
|
|
42
|
+
def initialize(source, file: '-e', known_subs: {}, implicit_semicolon: false)
|
|
43
|
+
@source = source
|
|
44
|
+
@file = file
|
|
45
|
+
@known_subs = known_subs
|
|
46
|
+
@implicit_semicolon_enabled = implicit_semicolon
|
|
47
|
+
@index = 0
|
|
48
|
+
@line = 1
|
|
49
|
+
@column = 1
|
|
50
|
+
@state = State.new
|
|
51
|
+
@pending_heredocs = []
|
|
52
|
+
@indirect_print = false
|
|
53
|
+
@data = nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def tokens
|
|
57
|
+
each.to_a
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def each
|
|
61
|
+
return enum_for(__method__) unless block_given?
|
|
62
|
+
|
|
63
|
+
while (token = next_token)
|
|
64
|
+
yield token
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def next_token
|
|
69
|
+
skip_ignored
|
|
70
|
+
return eof if end?
|
|
71
|
+
return implicit_block_semicolon if implicit_block_semicolon?
|
|
72
|
+
|
|
73
|
+
line = @line
|
|
74
|
+
column = @column
|
|
75
|
+
type, value = scan_token
|
|
76
|
+
@last_token_type = type
|
|
77
|
+
Token.new(type, value, @file, line, column)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def implicit_block_semicolon?
|
|
83
|
+
@implicit_semicolon_enabled && current == '}' && %i[block anon_sub].include?(@state.brackets.last&.kind) &&
|
|
84
|
+
@implicit_block_index != @index &&
|
|
85
|
+
!%i[; {].include?(@last_token_type)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def implicit_block_semicolon
|
|
89
|
+
@implicit_block_index = @index
|
|
90
|
+
@last_token_type = :';'
|
|
91
|
+
@indirect_print = false
|
|
92
|
+
@state.statement!
|
|
93
|
+
Token.new(:';', ';', @file, @line, @column)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def scan_token
|
|
97
|
+
scanners.each do |scanner|
|
|
98
|
+
token = send(scanner)
|
|
99
|
+
return token if token
|
|
100
|
+
end
|
|
101
|
+
illegal_character
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def scanners
|
|
105
|
+
%i[scan_end_marker scan_prototype scan_heredoc scan_readline scan_quote scan_number scan_variable scan_filetest
|
|
106
|
+
scan_operator scan_word scan_bracket]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def eof
|
|
110
|
+
if @state.brackets.empty?
|
|
111
|
+
return nil unless @implicit_semicolon_enabled
|
|
112
|
+
return nil if @implicit_semicolon || @last_token_type.nil? || @last_token_type == :';'
|
|
113
|
+
return nil if @last_token_type == :'}' && @state.expect != :operator
|
|
114
|
+
|
|
115
|
+
@implicit_semicolon = true
|
|
116
|
+
return Token.new(:';', ';', @file, @line, @column)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
frame = @state.brackets.last
|
|
120
|
+
raise CompileError, "Unmatched #{frame.open} at #{@file} line #{frame.line}."
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
data/lib/peruby/node.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Peruby
|
|
4
|
+
# Syntax tree node types.
|
|
5
|
+
module Node
|
|
6
|
+
# Recursive S-expression conversion shared by all immutable nodes.
|
|
7
|
+
module SExpression
|
|
8
|
+
def to_sexp
|
|
9
|
+
name = self.class.name.split('::').last.gsub(/([A-Z]+)([A-Z][a-z])/, '\\1_\\2')
|
|
10
|
+
.gsub(/([a-z\d])([A-Z])/, '\\1_\\2').downcase.to_sym
|
|
11
|
+
values = members.reject { |member| %i[file line].include?(member) }.map { |member| sexp(public_send(member)) }
|
|
12
|
+
[name, *values]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def sexp(value)
|
|
18
|
+
return value.to_sexp if value.respond_to?(:to_sexp)
|
|
19
|
+
return value.map { |item| sexp(item) } if value.is_a?(Array)
|
|
20
|
+
|
|
21
|
+
value
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.define(name, *members)
|
|
26
|
+
const_set(name, Data.define(*members, :file, :line) { include SExpression })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
define :Program, :statements
|
|
30
|
+
define :Nop
|
|
31
|
+
define :Literal, :value
|
|
32
|
+
define :Bareword, :value
|
|
33
|
+
define :Variable, :sigil, :name
|
|
34
|
+
define :Unary, :operator, :expression
|
|
35
|
+
define :Binary, :operator, :left, :right
|
|
36
|
+
define :Ternary, :condition, :true_expression, :false_expression
|
|
37
|
+
define :Assign, :operator, :left, :right
|
|
38
|
+
define :List, :items
|
|
39
|
+
define :Block, :statements
|
|
40
|
+
define :BareBlock, :body, :label
|
|
41
|
+
define :If, :condition, :then_block, :else_block
|
|
42
|
+
define :While, :condition, :body, :until_loop, :continue_block, :label
|
|
43
|
+
define :CFor, :initializer, :condition, :step, :body, :continue_block, :label
|
|
44
|
+
define :My, :variable, :initializer
|
|
45
|
+
define :State, :variable, :initializer
|
|
46
|
+
define :Return, :expression
|
|
47
|
+
define :SubDef, :name, :prototype, :body
|
|
48
|
+
define :AnonSub, :prototype, :body
|
|
49
|
+
define :ForEach, :variable, :list, :body, :continue_block, :label, :declaration
|
|
50
|
+
define :LoopJump, :kind, :label
|
|
51
|
+
define :ModifierCondition, :expression, :condition, :kind
|
|
52
|
+
define :Package, :name
|
|
53
|
+
define :PackageBlock, :name, :body
|
|
54
|
+
define :Print, :handle, :arguments
|
|
55
|
+
define :Call, :name, :arguments
|
|
56
|
+
define :CodeCall, :receiver, :arguments
|
|
57
|
+
define :Element, :container, :key, :kind, :dereference
|
|
58
|
+
define :MethodCall, :receiver, :name, :arguments
|
|
59
|
+
define :ArrayLiteral, :items
|
|
60
|
+
define :HashLiteral, :items
|
|
61
|
+
define :Match, :quote
|
|
62
|
+
define :Bind, :subject, :pattern, :negated
|
|
63
|
+
define :Regexp, :quote
|
|
64
|
+
define :Substitute, :quote
|
|
65
|
+
define :Transliterate, :quote
|
|
66
|
+
define :Dereference, :kind, :name
|
|
67
|
+
define :DereferenceExpr, :kind, :expression
|
|
68
|
+
define :Group, :expression
|
|
69
|
+
define :Our, :variable, :initializer
|
|
70
|
+
define :Local, :target, :initializer
|
|
71
|
+
define :Goto, :name
|
|
72
|
+
define :Readline, :handle
|
|
73
|
+
define :FileTest, :operator, :expression
|
|
74
|
+
define :Printf, :handle, :arguments
|
|
75
|
+
define :Say, :handle, :arguments
|
|
76
|
+
define :Postfix, :operator, :expression
|
|
77
|
+
define :Prefix, :operator, :expression
|
|
78
|
+
define :ModifierFor, :expression, :list
|
|
79
|
+
define :Map, :body, :list, :grep
|
|
80
|
+
define :Sort, :comparator, :list, :package
|
|
81
|
+
define :Use, :module_name, :imports, :disable, :package
|
|
82
|
+
define :Require, :expression
|
|
83
|
+
define :Eval, :body, :string
|
|
84
|
+
define :Constant, :name, :value
|
|
85
|
+
|
|
86
|
+
Bareword.define_method(:to_sexp) { [:literal, value] }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Peruby
|
|
4
|
+
module Op
|
|
5
|
+
# Scalar plain or compound assignment.
|
|
6
|
+
class Assign < Base
|
|
7
|
+
def initialize(operator, left, right)
|
|
8
|
+
@operator = operator
|
|
9
|
+
@left = left
|
|
10
|
+
@right = right
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(env, context)
|
|
14
|
+
return assign_aggregate(env, context) if @operator == :'=' && @left.respond_to?(:aggregate_assignment?) &&
|
|
15
|
+
@left.aggregate_assignment?
|
|
16
|
+
|
|
17
|
+
target = @left.lvalue(env, :list)
|
|
18
|
+
return assign_list(target, env, context) if target.is_a?(Array)
|
|
19
|
+
|
|
20
|
+
return logical_assignment(target, env, context) if logical_assignment?
|
|
21
|
+
|
|
22
|
+
value = @operator == :'=' ? @right.run(env, :scalar) : assigned_value(target.get, @right.run(env, :scalar))
|
|
23
|
+
target.set(value)
|
|
24
|
+
context == :list ? [target] : value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_callable
|
|
28
|
+
return super if logical_assignment?
|
|
29
|
+
|
|
30
|
+
right = @right.to_callable
|
|
31
|
+
scalar_right = @right.scalar_callable if @right.respond_to?(:scalar_callable)
|
|
32
|
+
left = @left.respond_to?(:lvalue_callable) ? @left.lvalue_callable : ->(env) { @left.lvalue(env, :list) }
|
|
33
|
+
lambda do |env, context|
|
|
34
|
+
if @operator == :'=' && @left.respond_to?(:aggregate_assignment?) && @left.aggregate_assignment?
|
|
35
|
+
next assign_aggregate(env, context, right.call(env, :list))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
target = left.call(env)
|
|
39
|
+
next assign_list(target, env, context) if target.is_a?(Array)
|
|
40
|
+
|
|
41
|
+
value = if @operator == :'='
|
|
42
|
+
scalar_right ? scalar_right.call(env) : right.call(env, :scalar)
|
|
43
|
+
else
|
|
44
|
+
compiled_value(target.value, scalar_right ? scalar_right.call(env) : right.call(env, :scalar))
|
|
45
|
+
end
|
|
46
|
+
target.set(value)
|
|
47
|
+
context == :list ? [target] : value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_void_callable
|
|
52
|
+
return ->(env) { run(env, :void) } if logical_assignment?
|
|
53
|
+
|
|
54
|
+
right = @right.to_callable
|
|
55
|
+
scalar_right = @right.scalar_callable if @right.respond_to?(:scalar_callable)
|
|
56
|
+
left = @left.respond_to?(:lvalue_callable) ? @left.lvalue_callable : ->(env) { @left.lvalue(env, :list) }
|
|
57
|
+
lambda do |env|
|
|
58
|
+
if @operator == :'=' && @left.respond_to?(:aggregate_assignment?) && @left.aggregate_assignment?
|
|
59
|
+
next assign_aggregate(env, :void, right.call(env, :list))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
target = left.call(env)
|
|
63
|
+
next assign_list(target, env, :void) if target.is_a?(Array)
|
|
64
|
+
|
|
65
|
+
right_value = scalar_right ? scalar_right.call(env) : right.call(env, :scalar)
|
|
66
|
+
value = @operator == :'=' ? right_value : compiled_value(target.value, right_value)
|
|
67
|
+
target.set(value)
|
|
68
|
+
value
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def compiled_value(left, right)
|
|
75
|
+
if left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
76
|
+
return Conv.norm_int(left + right) if @operator == :'+='
|
|
77
|
+
return Conv.norm_int(left - right) if @operator == :'-='
|
|
78
|
+
return Conv.norm_int(left * right) if @operator == :'*='
|
|
79
|
+
return Conv.divide(left, right) if @operator == :'/='
|
|
80
|
+
return Conv.modulo(left, right) if @operator == :'%='
|
|
81
|
+
end
|
|
82
|
+
assigned_value(left, right)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def assign_aggregate(env, context, cells = @right.run(env, :list))
|
|
86
|
+
values = cells.map(&:get)
|
|
87
|
+
@left.assign_values(env, values)
|
|
88
|
+
context == :list ? values.map { |value| Scalar.new(value) } : values.length
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def assign_list(targets, env, context)
|
|
92
|
+
raise PerlError, 'Modification of a read-only value attempted' unless @operator == :'='
|
|
93
|
+
|
|
94
|
+
values = @right.run(env, :list).map(&:get)
|
|
95
|
+
targets.each_with_index { |target, index| target.set(values[index]) }
|
|
96
|
+
context == :list ? targets : values.length
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def assigned_value(left, right) # rubocop:disable Metrics/AbcSize
|
|
100
|
+
case @operator.to_s
|
|
101
|
+
when '+=' then Conv.norm_int(Conv.to_num(left) + Conv.to_num(right))
|
|
102
|
+
when '-=' then Conv.norm_int(Conv.to_num(left) - Conv.to_num(right))
|
|
103
|
+
when '*=' then Conv.norm_int(Conv.to_num(left) * Conv.to_num(right))
|
|
104
|
+
when '/=' then Conv.divide(left, right)
|
|
105
|
+
when '%=' then Conv.modulo(left, right)
|
|
106
|
+
when '.=' then Conv.to_str(left) + Conv.to_str(right)
|
|
107
|
+
when 'x=' then Conv.repeat(left, right)
|
|
108
|
+
when '**=' then Conv.norm_int(Conv.to_num(left)**Conv.to_num(right))
|
|
109
|
+
when '<<=' then Conv.to_num(left).to_i << Conv.to_num(right).to_i
|
|
110
|
+
when '>>=' then Conv.to_num(left).to_i >> Conv.to_num(right).to_i
|
|
111
|
+
when '&=' then Conv.bitwise(:&, left, right)
|
|
112
|
+
when '|=' then Conv.bitwise(:|, left, right)
|
|
113
|
+
when '^=' then Conv.bitwise(:^, left, right)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def logical_assignment?(operator = @operator) = %i[&&= ||= //=].include?(operator)
|
|
118
|
+
|
|
119
|
+
def logical_assignment(target, env, context)
|
|
120
|
+
current = target.get
|
|
121
|
+
assign = (@operator == :'&&=' && Conv.truthy?(current)) || (@operator == :'||=' && !Conv.truthy?(current)) ||
|
|
122
|
+
(@operator == :'//=' && current.nil?)
|
|
123
|
+
target.set(@right.run(env, :scalar)) if assign
|
|
124
|
+
context == :list ? [target] : target.get
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|