Skip to content

Commit a67cbd1

Browse files
committed
improved rubyish method and var parsing.
1 parent c8b4012 commit a67cbd1

File tree

6 files changed

+124
-88
lines changed

6 files changed

+124
-88
lines changed

examples/rubyish/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ Implemented:
2020
- nqp opcode calls: `nqp::sleep(5)`
2121
- a few built-ins: `abort`, `print`, `puts`, `sleep`
2222
- a couple of methods: `.call` and `.nil?`
23-
- infixish assigments: `+=` `-=` `*=` ...
23+
- infixish assignments: `+=` `-=` `*=` ...
2424
- simple classes and objects with attributes.
25-
- inheritence (no mixins yet) - see [inheritance.t](t/inheritance.t)
25+
- inheritance (no mixins yet) - see [inheritance.t](t/inheritance.t)
2626
- `while` and `until` loops
2727
- statement modifiers `if` `unless`, `while`, `until` e.g.: `puts 42 if true`
2828
- basic arrays and hashes
@@ -52,7 +52,7 @@ Strings and truth values are Perlish rather than Rubyish:
5252
- `+` always does addition (doesn't concatenate strings)
5353
- `~` has been introduced as the concatenation operator
5454
- `>`, `==`, `<=` ... only do arithmetic comparisons
55-
- `gt`, `eq`, `le` ... do string comparisions
55+
- `gt`, `eq`, `le` ... do string comparisons
5656
- 0, '0', '' are false in a boolean context.
5757
- hash dereferencing is via angle braces: `puts fruit<apples>` or
5858
curlies `puts fruit{'bananas'}`
@@ -77,8 +77,7 @@ this includes nqp control-flow functions:
7777
end
7878
```
7979

80-
There's some context sensitive parsing to distinguish functions and
81-
expressions, E.g.
80+
Rubyish does a limited amout of context sensitive parsing, E.g.
8281
```
8382
yy = 37
8483
puts yy -5 # parsed as an expression; output is 32
@@ -87,3 +86,6 @@ expressions, E.g.
8786
puts xx -5 # parsed as a method call; output is 42
8887
```
8988

89+
This only works only if the functions and methods have been previously declared.
90+
91+
If in doubt, use parenthesis for function calls, `capitalize(name)` vs `capitalize name`; and make method calls explicit, `self.fibonacci(n - 1)` vs `fibonacci(n - 1)`.

examples/rubyish/rubyish.nqp

Lines changed: 108 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ class RubyishClassHOW {
1111
has %!methods;
1212

1313
method new_type(:$name!, :$isa?) {
14-
nqp::die("duplicate class definition: $name")
15-
if %CLASSES{ $name };
14+
nqp::die("duplicate class definition: $name")
15+
if %CLASSES{ $name };
1616

17-
my $obj := self.new(:$name, :$isa);
18-
%CLASSES{ $name } := [$obj];
17+
my $obj := self.new(:$name, :$isa);
18+
%CLASSES{ $name } := [$obj];
1919

2020
nqp::newtype($obj, 'HashAttrStore');
2121
}
@@ -29,24 +29,24 @@ class RubyishClassHOW {
2929

3030
method find_method($obj, $name) {
3131

32-
my $method;
32+
my $method;
3333

34-
if nqp::substr($name, 0, 1) eq '^' {
35-
# '^' prefix indicates a superclass lookup
36-
$name := nqp::substr($name, 1);
37-
}
38-
else {
39-
$method := %!methods{$name};
40-
}
34+
if nqp::substr($name, 0, 1) eq '^' {
35+
# '^' prefix indicates a superclass lookup
36+
$name := nqp::substr($name, 1);
37+
}
38+
else {
39+
$method := %!methods{$name};
40+
}
4141

42-
if !$method && $!isa {
43-
my $super := %CLASSES{ $!isa };
44-
nqp::die("unresolved super-class: " ~ $!isa)
45-
unless $super;
46-
$method := $super[0].find_method( $obj, $name);
47-
}
42+
if !$method && $!isa {
43+
my $super := %CLASSES{ $!isa };
44+
nqp::die("unresolved super-class: " ~ $!isa)
45+
unless $super;
46+
$method := $super[0].find_method( $obj, $name);
47+
}
4848

49-
$method // nqp::null();
49+
$method // nqp::null();
5050
}
5151
}
5252

@@ -58,7 +58,9 @@ grammar Rubyish::Grammar is HLL::Grammar {
5858
:my $*CLASS_BLOCK := $*CUR_BLOCK;
5959
:my $*IN_TEMPLATE := 0;
6060
:my $*IN_PARENS := 0;
61-
:my %*SYM := self.sym-init();
61+
:my %*SYM;
62+
:my %*CLASS_SYMS;
63+
6264
^ ~ $ <stmtlist>
6365
|| <.panic('Syntax error')>
6466
}
@@ -92,17 +94,22 @@ grammar Rubyish::Grammar is HLL::Grammar {
9294
proto token stmt {*}
9395

9496
token stmt:sym<def> {:s
95-
:my %sym-save := self.hcopy(%*SYM);
9697

97-
'def' ~ 'end' <defbody>
98+
:my %sym-save := nqp::clone(%*SYM);
99+
:my $*DEF;
98100

99-
{%*SYM := self.hcopy(%sym-save)}
101+
'def' ~ 'end' <defbody> {
102+
%sym-save{$*DEF} := %*SYM{$*DEF};
103+
%*SYM := %sym-save;
104+
}
100105
}
101106

102107
rule defbody {
103108
:my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new());
104-
:my $*DEF;
105-
<operation> {$*DEF := ~$<operation>}
109+
<operation> {
110+
$*DEF := ~$<operation>;
111+
%*SYM{$*DEF} := $*IN_CLASS ?? 'method' !! 'func';
112+
}
106113
['(' ~ ')' <signature>?]? <separator>?
107114
<stmtlist>
108115
}
@@ -116,26 +123,38 @@ grammar Rubyish::Grammar is HLL::Grammar {
116123
| '&' <func=.param>
117124
}
118125

119-
token param { <ident> [:s<hs> '=' <EXPR>]?}
126+
token param { <ident> [:s<hs> '=' <EXPR>]? {
127+
%*SYM{~$<ident>} := 'var'
128+
}
129+
}
120130

121131
token stmt:sym<class> {
122132
:my $*IN_CLASS := 1;
123133
:my @*METHODS;
124-
:my %sym-save := self.hcopy(%*SYM);
134+
:my %sym-save := nqp::clone(%*SYM);
125135

126-
[<sym> \h+] ~ [\h* 'end'] <classbody>
127-
128-
{%*SYM := self.hcopy(%sym-save)}
136+
[<sym> \h+] ~ [\h* 'end'] <classbody> {
137+
%*SYM := %sym-save
138+
}
129139
}
130140

131141
rule classbody {
132142
:my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new());
133143
:my $*CLASS_BLOCK := $*CUR_BLOCK;
134144

135145
<ident> { $*CLASS_BLOCK.name(~$<ident>) }
136-
[ '<' <super=.ident> ]?
146+
[ '<' <super=.ident> { inherit-syms(~$<super>) } ]?
137147
<separator>
138-
<stmtlist>
148+
<stmtlist> {
149+
%*CLASS_SYMS{~$<ident>} := %*SYM;
150+
}
151+
}
152+
153+
sub inherit-syms($class) {
154+
if my %syms := %*CLASS_SYMS{$class} {
155+
%*SYM{$_} := %syms{$_}
156+
for %syms;
157+
}
139158
}
140159

141160
token stmt:sym<EXPR> { <EXPR> }
@@ -146,15 +165,28 @@ grammar Rubyish::Grammar is HLL::Grammar {
146165
<closure>
147166
}
148167

168+
my %builtins;
169+
170+
method callable($op) {
171+
%builtins := self.builtin-init()
172+
unless %builtins<puts>;
173+
174+
my $type := %*SYM{$op} || (%builtins{$op} && 'func');
175+
176+
$type && ($type eq 'func' || $type eq 'method');
177+
}
178+
149179
token term:sym<call> {
150180
<!keyword>
151181
<operation> ['(' ~ ')' <call-args=.paren-args>? <code-block>?
152-
|:s<hs> <call-args>? <?{%*SYM{~$<operation>} eq 'def'}> ]
182+
|:s<hs> <call-args>? <?{self.callable(~$<operation>)}>
183+
]
153184
}
154185

155186
token term:sym<super> {
156187
'super' ['(' ~ ')' <call-args=.paren-args>? <code-block>?
157-
|:s <call-args>? ]
188+
|:s <call-args>?
189+
]
158190
}
159191

160192
token term:sym<nqp-op> {
@@ -166,7 +198,7 @@ grammar Rubyish::Grammar is HLL::Grammar {
166198
}
167199

168200
token call-args {:s<hs>
169-
[<arg=.hash-args>||<arg=.EXPR>]+ % ',' [ ',' <arg=.func-ref> ]?
201+
[ <arg=.hash-args>||<arg=.EXPR>]+ % ',' [ ',' <arg=.func-ref> ]?
170202
| <arg=.func-ref>
171203
}
172204

@@ -186,7 +218,7 @@ grammar Rubyish::Grammar is HLL::Grammar {
186218
:my $*MAYBE_DECL := 0;
187219
\+?
188220
$<sigil>=[ \$ | \@\@? | <!keyword> ]
189-
<ident>
221+
<ident><!before [\!|\?|\h*\(]>
190222
[ <?before \h* '=' [\w | \h+ || <.EXPR>] { $*MAYBE_DECL := 1 }> || <?> ]
191223
}
192224

@@ -212,9 +244,9 @@ grammar Rubyish::Grammar is HLL::Grammar {
212244
\n$<marker>$$
213245
}
214246

215-
token chars {\n? [<!before ['#{']> \N]+ | \n }
247+
token heredoc-line {\n? [<!before ['#{']> \N]+ | \n }
216248
token heredoc:sym<interp> {\" $<marker>=<- [\" \n]>+? \"\n
217-
[<text=.interp> | <text=.chars> ]*?
249+
[<text=.interp> | <text=.heredoc-line> ]*?
218250
\n$<marker>$$
219251
}
220252

@@ -246,6 +278,7 @@ grammar Rubyish::Grammar is HLL::Grammar {
246278
| begin | else | in | rescue | undef
247279
| break | elsif | module | retry | unless
248280
| case | end | next | return | until
281+
| eq | ne | lt | gt | le | ge | cmp
249282
] <!ww>
250283
}
251284

@@ -380,7 +413,10 @@ grammar Rubyish::Grammar is HLL::Grammar {
380413
}
381414

382415
token stmt:sym<for> {:s
383-
<sym> <ident> 'in' <EXPR> <do-block>
416+
<sym> <ident> 'in' <EXPR> {
417+
%*SYM{~$<ident>} := 'var'
418+
}
419+
<do-block>
384420
}
385421

386422
token do-block { <do> ~ 'end' <stmtlist> }
@@ -414,18 +450,6 @@ grammar Rubyish::Grammar is HLL::Grammar {
414450
);
415451
}
416452

417-
method sym-init() {
418-
my %builtins := self.builtin-init();
419-
my %sym;
420-
%sym{$_} := 'def' for %builtins;
421-
return %sym;
422-
}
423-
424-
method hcopy(%in) {
425-
my %out;
426-
%out{$_} := %in{$_} for %in;
427-
return %out;
428-
}
429453
}
430454

431455
class Rubyish::Actions is HLL::Actions {
@@ -482,9 +506,20 @@ class Rubyish::Actions is HLL::Actions {
482506
unless %builtins<puts>;
483507
my $op := %builtins{$name};
484508

485-
my $call := $op
486-
?? QAST::Op.new( :op($op) )
487-
!! QAST::Op.new( :op('call'), :name($name) );
509+
my $call;
510+
511+
if $op {
512+
$call := QAST::Op.new( :op($op) )
513+
}
514+
elsif %*SYM{$name} eq 'method' {
515+
$call := QAST::Op.new( :op('callmethod'),
516+
QAST::Var.new( :name('self'), :scope('lexical')),
517+
QAST::SVal.new( :value($name) ),
518+
);
519+
}
520+
else {
521+
$call := QAST::Op.new( :op('call'), :name($name) );
522+
}
488523

489524
if $<call-args> {
490525
$call.push($_)
@@ -501,9 +536,9 @@ class Rubyish::Actions is HLL::Actions {
501536
my $name := ~$*DEF;
502537

503538
my $call := QAST::Op.new( :op('callmethod'),
504-
QAST::Var.new( :name('self'), :scope('lexical')),
505-
QAST::SVal.new( :value('^' ~ $name) ),
506-
);
539+
QAST::Var.new( :name('self'), :scope('lexical')),
540+
QAST::SVal.new( :value('^' ~ $name) ),
541+
);
507542

508543
if $<call-args> {
509544
$call.push($_)
@@ -580,15 +615,15 @@ class Rubyish::Actions is HLL::Actions {
580615
),
581616

582617
# call initialize method, if available
583-
($<call-args>
584-
?? $init-call
585-
!! QAST::Op.new( :op<if>,
586-
QAST::Op.new( :op<can>,
587-
QAST::Var.new( :name($tmp-sym), :scope<lexical> ),
588-
QAST::SVal.new( :value<initialize> )),
589-
$init-call,
590-
)
591-
),
618+
($<call-args>
619+
?? $init-call
620+
!! QAST::Op.new( :op<if>,
621+
QAST::Op.new( :op<can>,
622+
QAST::Var.new( :name($tmp-sym), :scope<lexical> ),
623+
QAST::SVal.new( :value<initialize> )),
624+
$init-call,
625+
)
626+
),
592627

593628
# return the new object
594629
QAST::Var.new( :name($tmp-sym), :scope<lexical> ),
@@ -653,7 +688,6 @@ class Rubyish::Actions is HLL::Actions {
653688
QAST::Var.new( :name($install.name), :scope('lexical'), :decl('var') ),
654689
$install
655690
));
656-
%*SYM{$install.name} := 'def';
657691
if $*IN_CLASS {
658692
@*METHODS.push($install);
659693
}
@@ -678,6 +712,7 @@ class Rubyish::Actions is HLL::Actions {
678712
my $var := QAST::Var.new(
679713
:name(~$<ident>), :scope('lexical'), :decl('param')
680714
);
715+
$*CUR_BLOCK.symbol('self', :declared(1));
681716

682717
$var.default( $<EXPR>.ast )
683718
if $<EXPR>;
@@ -722,9 +757,9 @@ class Rubyish::Actions is HLL::Actions {
722757
QAST::SVal.new( :value(~$<classbody><ident>), :named('name') ),
723758
);
724759

725-
$new_type.push(
726-
QAST::SVal.new( :value(~$<classbody><super>), :named('isa') )
727-
) if ~$<classbody><super>;
760+
$new_type.push(
761+
QAST::SVal.new( :value(~$<classbody><super>), :named('isa') )
762+
) if $<classbody><super>;
728763

729764
$class_stmts.push(QAST::Op.new(
730765
:op('bind'),
@@ -736,15 +771,15 @@ class Rubyish::Actions is HLL::Actions {
736771
my $class_var := QAST::Var.new( :name($ins_name), :scope('lexical') );
737772

738773
for @*METHODS {
739-
my $name := $_.name;
774+
my $name := $_.name;
740775

741776
$class_stmts.push(QAST::Op.new(
742777
:op('callmethod'), :name('add_method'),
743778
QAST::Op.new( :op('how'), $class_var ),
744779
$class_var,
745780
QAST::SVal.new( :value($name) ),
746781
QAST::BVal.new( :value($_) ))
747-
);
782+
);
748783
}
749784

750785
make $class_stmts;
@@ -790,7 +825,7 @@ class Rubyish::Actions is HLL::Actions {
790825
make QAST::SVal.new( :value( ~$<text> ) );
791826
}
792827

793-
method chars ($/) { make QAST::SVal.new( :value(~$/) ) }
828+
method heredoc-line($/) { make QAST::SVal.new( :value(~$/) ) }
794829

795830
method heredoc:sym<interp>($/) {
796831
my $list := QAST::Op.new( :op<list> );

0 commit comments

Comments
 (0)