Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
basic rubyish inheritance (no mixins yet)
  • Loading branch information
dwarring committed Nov 24, 2013
1 parent 2ad90ea commit f4514bb
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 14 deletions.
3 changes: 2 additions & 1 deletion examples/rubyish/README.md
Expand Up @@ -21,7 +21,8 @@ Implemented:
- a few built-ins: `abort`, `print`, `puts`, `sleep`
- a couple of methods: `.call` and `.nil?`
- infixish assigments: `+=` `-=` `*=` ...
- very simple classes and objects with attributes. no inheritence
- simple classes and objects with attributes.
- inheritence (no mixins yet) - see [inheritance.t](t/inheritance.t)
- `while` and `until` loops
- statement modifiers `if` `unless`, `while`, `until` e.g.: `puts 42 if true`
- basic arrays and hashes
Expand Down
90 changes: 77 additions & 13 deletions examples/rubyish/rubyish.nqp
Expand Up @@ -3,12 +3,21 @@ use NQPHLL;
# Ruby subset extended from the `rubyish` example, as introduced in the
# Edument Rakudo and NQP internals course.

my %CLASSES;

class RubyishClassHOW {
has $!name;
has $!isa;
has %!methods;

method new_type(:$name!) {
nqp::newtype(self.new(:$name), 'HashAttrStore')
method new_type(:$name!, :$isa?) {
nqp::die("duplicate class definition: $name")
if %CLASSES{ $name };

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

nqp::newtype($obj, 'HashAttrStore');
}

method add_method($obj, $name, $code) {
Expand All @@ -19,7 +28,25 @@ class RubyishClassHOW {
}

method find_method($obj, $name) {
%!methods{$name} // nqp::null();

my $method;

if nqp::substr($name, 0, 1) eq '^' {
# '^' prefix indicates a superclass lookup
$name := nqp::substr($name, 1);
}
else {
$method := %!methods{$name};
}

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

$method // nqp::null();
}
}

Expand Down Expand Up @@ -74,7 +101,9 @@ grammar Rubyish::Grammar is HLL::Grammar {

rule defbody {
:my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new());
<operation> ['(' ~ ')' <signature>?]? <separator>?
:my $*DEF;
<operation> {$*DEF := ~$<operation>}
['(' ~ ')' <signature>?]? <separator>?
<stmtlist>
}

Expand All @@ -96,14 +125,15 @@ grammar Rubyish::Grammar is HLL::Grammar {

[<sym> \h+] ~ [\h* 'end'] <classbody>

{%*SYM := self.hcopy(%sym-save);1}
{%*SYM := self.hcopy(%sym-save)}
}

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

<ident> { $*CLASS_BLOCK.name(~$<ident>) }
[ '<' <super=.ident> ]?
<separator>
<stmtlist>
}
Expand All @@ -122,6 +152,11 @@ grammar Rubyish::Grammar is HLL::Grammar {
|:s<hs> <call-args>? <?{%*SYM{~$<operation>} eq 'def'}> ]
}

token term:sym<super> {
'super' ['(' ~ ')' <call-args=.paren-args>? <code-block>?
|:s <call-args>? ]
}

token term:sym<nqp-op> {
'nqp::'<ident> ['(' ~ ')' <call-args=.paren-args>? | <call-args>? ]
}
Expand Down Expand Up @@ -462,6 +497,25 @@ class Rubyish::Actions is HLL::Actions {
make $call;
}

method term:sym<super>($/) {
my $name := ~$*DEF;

my $call := QAST::Op.new( :op('callmethod'),
QAST::Var.new( :name('self'), :scope('lexical')),
QAST::SVal.new( :value('^' ~ $name) ),
);

if $<call-args> {
$call.push($_)
for $<call-args>.ast;
}

$call.push( $<code-block>.ast )
if $<code-block>;

make $call;
}

method term:sym<nqp-op>($/) {
my $op := ~$<ident>;
my $call := QAST::Op.new( :op($op) );
Expand Down Expand Up @@ -526,12 +580,15 @@ class Rubyish::Actions is HLL::Actions {
),

# call initialize method, if available
QAST::Op.new( :op<if>,
QAST::Op.new( :op<can>,
QAST::Var.new( :name($tmp-sym), :scope<lexical> ),
QAST::SVal.new( :value<initialize> )),
$init-call,
),
($<call-args>
?? $init-call
!! QAST::Op.new( :op<if>,
QAST::Op.new( :op<can>,
QAST::Var.new( :name($tmp-sym), :scope<lexical> ),
QAST::SVal.new( :value<initialize> )),
$init-call,
)
),

# return the new object
QAST::Var.new( :name($tmp-sym), :scope<lexical> ),
Expand Down Expand Up @@ -665,6 +722,10 @@ class Rubyish::Actions is HLL::Actions {
QAST::SVal.new( :value(~$<classbody><ident>), :named('name') ),
);

$new_type.push(
QAST::SVal.new( :value(~$<classbody><super>), :named('isa') )
) if ~$<classbody><super>;

$class_stmts.push(QAST::Op.new(
:op('bind'),
QAST::Var.new( :name($ins_name), :scope('lexical'), :decl('var') ),
Expand All @@ -675,12 +736,15 @@ class Rubyish::Actions is HLL::Actions {
my $class_var := QAST::Var.new( :name($ins_name), :scope('lexical') );

for @*METHODS {
my $name := $_.name;

$class_stmts.push(QAST::Op.new(
:op('callmethod'), :name('add_method'),
QAST::Op.new( :op('how'), $class_var ),
$class_var,
QAST::SVal.new( :value($_.name) ),
QAST::BVal.new( :value($_) )));
QAST::SVal.new( :value($name) ),
QAST::BVal.new( :value($_) ))
);
}

make $class_stmts;
Expand Down
43 changes: 43 additions & 0 deletions examples/rubyish/t/inheritance.t
@@ -0,0 +1,43 @@
puts "1..10"

class Point
@@tst = -1
def initialize(x, y)
puts "ok #{ @@tst = @@tst + 2 } - Point.initialize called"
@x = x
@y = y
end

def x; @x ; end
def y; @y ; end

def theta; (@x * @x + @y * @y) ** 0.5; end
end

class Point3D < Point
def initialize(x, y, z)
puts "ok 2 - Point3D.initialize called"
super(x, y)
@z = z
end

def z; @z ; end

def theta; (@x * @x + @y * @y + @z * @z) ** 0.5; end
end

obj_2d = Point.new(10, 20)
obj_3d = Point3D.new(15, 25, 35)

puts "#{obj_2d.x == 10 ? 'ok' : 'nok'} 4 - 2d obj x"
puts "#{obj_2d.y == 20 ? 'ok' : 'nok'} 5 - 2d obj y"

t2 = obj_2d.theta
puts "#{t2 > 22 && t2 < 23 ? 'ok' : 'nok'} 6 - theta 2d (approx)"

puts "#{obj_3d.x == 15 ? 'ok' : 'nok'} 7 - 3d obj x"
puts "#{obj_3d.y == 25 ? 'ok' : 'nok'} 8 - 3d obj y"
puts "#{obj_3d.z == 35 ? 'ok' : 'nok'} 9 - 3d obj z"

t3 = obj_3d.theta
puts "#{t3 > 45 && t2 < 46 ? 'ok' : 'nok'} 10 - theta 3d (approx)"

0 comments on commit f4514bb

Please sign in to comment.