Skip to content

Commit

Permalink
make declaration, assignment, and say() work
Browse files Browse the repository at this point in the history
  • Loading branch information
Carl Masak committed Dec 8, 2014
1 parent e91d810 commit 445f068
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 0 deletions.
123 changes: 123 additions & 0 deletions lib/_007.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
role Val {}
role Val::None is Val {}
role Val::Int is Val { has Int $.value }
role Val::Str is Val { has Str $.value }

sub children(*@c) {
"\n" ~ @c.join("\n").indent(2)
}

role Q {
}

role Q::Literal::Int does Q {
has $.value;
method new(Int $value) { self.bless(:$value) }
method Str { "Int[$.value]" }
method eval($) { Val::Int.new(:$.value) }
}

role Q::Literal::Str does Q {
has $.value;
method new(Str $value) { self.bless(:$value) }
method Str { qq[Str["$.value"]] }
method eval($) { Val::Str.new(:$.value) }
}

role Q::Term::Identifier does Q {
has $.name;
method new(Str $name) { self.bless(:$name) }
method Str { "Identifier[$.name]" }
method eval($runtime) {
return $runtime.get-var($.name);
}
}

role Q::Expr::Assignment does Q {
has $.ident;
has $.expr;
method new($ident, $expr) { self.bless(:$ident, :$expr) }
method Str { "Assign" ~ children($.ident, $.expr) }
method eval($runtime) {
my $value = $.expr.eval($runtime);
$runtime.put-var($.ident.name, $value);
return $value;
}
}

role Q::Expr::Call::Sub does Q {
has $.ident;
has @.args;
method new($ident, *@args) { self.bless(:$ident, :@args) }
method Str { "Call" ~ children($.ident, |@.args) }
method eval($runtime) {
# TODO: de-hack -- wants to be a hash of builtins somewhere
die "Unknown sub {$.ident.name}"
unless $.ident.name eq "say";
my $arg = @.args[0].eval($runtime);
$runtime.output.say($arg.value);
Val::None.new;
}
}

role Q::Statement::Expr does Q {
has $.expr;
method new($expr) { self.bless(:$expr) }
method Str { "Expr" ~ children($.expr) }
method run($runtime) {
$.expr.eval($runtime);
}
}

role Q::Statement::VarDecl does Q {
has $.ident;
has $.assignment;
method new($ident, $assignment = Nil) { self.bless(:$ident, :$assignment) }
method Str { "VarDecl" ~ children($.ident, |$.assignment) }
method run($runtime) {
# TODO: should have an if statement here, but need a test case for it
$.assignment.eval($runtime);
}
}

role Q::CompUnit does Q {
has @.statements;
method new(*@statements) { self.bless(:@statements) }
method Str { "CompUnit" ~ children(@.statements) }
method run($runtime) {
for @.statements -> $statement {
$statement.run($runtime);
}
}
}

role Runtime {
has $.output;
has %!pad;

method run($compunit) {
$compunit.run(self);
}

method put-var($name, $value) {
%!pad{$name} = $value;
}

method get-var($name) {
return %!pad{$name};
}
}

role _007 {
method runtime(:$output = $*OUT) {
Runtime.new(:$output);
}
}
63 changes: 63 additions & 0 deletions lib/_007/Test.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use v6;
use _007;
use Test;

sub read(Str $ast) is export {
my %qclass_lookup =
int => Q::Literal::Int,
str => Q::Literal::Str,
ident => Q::Term::Identifier,

assign => Q::Expr::Assignment,
call => Q::Expr::Call::Sub,

vardecl => Q::Statement::VarDecl,
stexpr => Q::Statement::Expr,

compunit => Q::CompUnit,
;

my grammar _007::Syntax {
regex TOP { \s* <expr> \s* }
proto token expr {*}
token expr:list { '(' ~ ')' [<expr>+ % \s+] }
token expr:int { \d+ }
token expr:symbol { \w+ }
token expr:str { '"' ~ '"' (<-["]>+) }
}

my $actions = role {
method TOP($/) { make $<expr>.ast }
method expr:list ($/) {
my $qname = ~$<expr>[0];
die "Unknown name: $qname"
unless %qclass_lookup{$qname} :exists;
my $qclass = %qclass_lookup{$qname};
my @rest = $<expr>».ast[1..*];
make $qclass.new(|@rest);
}
method expr:symbol ($/) { make ~$/ }
method expr:int ($/) { make +$/ }
method expr:str ($/) { make ~$0 }
};

_007::Syntax.parse($ast, :$actions)
or die "failure";
return $/.ast;
}

role Output {
has $.result = "";

method say($s) { $!result ~= $s ~ "\n" }
}

sub is-result($input, $expected, $desc = "MISSING TEST DESCRIPTION") is export {
my $ast = read($input);
my $output = Output.new;
my $runtime = _007.runtime(:$output);
$runtime.run($ast, :$output);

is $output.result, $expected, $desc;
}

25 changes: 25 additions & 0 deletions t/semantics/types.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use v6;
use Test;
use _007::Test;

{
my $ast = q:to/./;
(compunit
(vardecl (ident "n") (assign (ident "n") (int 7)))
(stexpr (call (ident "say") (ident "n"))))
.

is-result $ast, "7\n", "int type works";
}

{
my $ast = q:to/./;
(compunit
(vardecl (ident "s") (assign (ident "s") (str "Bond")))
(stexpr (call (ident "say") (ident "s"))))
.

is-result $ast, "Bond\n", "str type works";
}

done;

0 comments on commit 445f068

Please sign in to comment.