Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Move to Moar from Parrot; unbitrot; much improved indent handling.
First is self-explanatory, second simply due to the code being untended for a
long time and easily fixed by cargo-culting a bit of code from Rakudo.

Much improved indent handling has been achieved by following the Pynie
approach much more closely. As a consequence, we now handle ifs with elses
properly (attaching a multiply dedented else to the correct parent rather than
the first available). Also adds a simple sanity test for if handling.
  • Loading branch information
arnsholt committed Oct 19, 2014
1 parent 85167cd commit 2f600b0
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 53 deletions.
7 changes: 2 additions & 5 deletions .gitignore
@@ -1,5 +1,2 @@
blib/*.c
blib/*.o
blib/*.pbc
blib/Snake/*.pbc
snake
blib/*.moarvm
blib/Snake/*.moarvm
30 changes: 15 additions & 15 deletions Makefile
@@ -1,21 +1,21 @@
NQP=../nqp/nqp
PARROT=../nqp/install/bin/parrot
PBC_TO_EXE=../nqp/install/bin/pbc_to_exe
PREFIX=../nqp/install
NQP=$(PREFIX)/bin/nqp-m
MOAR=$(PREFIX)/bin/moar

PBCS=blib/Snake/Actions.pbc \
blib/Snake/Compiler.pbc \
blib/Snake/Grammar.pbc \
MOARS=blib/Snake/Actions.moarvm \
blib/Snake/Compiler.moarvm \
blib/Snake/Grammar.moarvm \
blib/snake.moarvm

snake: $(PBCS) src/snake.nqp
$(NQP) --target=pir src/snake.nqp | $(PARROT) -o blib/snake.pbc -
$(PBC_TO_EXE) blib/snake.pbc
cp blib/snake $@
.PHONY: all

blib/%.pbc: src/%.nqp
$(NQP) --target=pir $< | $(PARROT) -o $@ -
all: $(MOARS)

clean:
rm -f $(PBCS)
blib/%.moarvm: src/%.nqp
$(NQP) --target=mbc --output=$@ $<

test:
test: all
prove -r --exec ./snake t/sanity/*.t

clean:
-rm $(MOARS)
6 changes: 6 additions & 0 deletions snake
@@ -0,0 +1,6 @@
#!/bin/bash

# Caveat emptor: Extremely ad hoc, won't ever work outside of snake root
# directory.
exec ../nqp/install/bin/moar --execname="$0" --libpath=blib \
--libpath=../nqp/install/languages/nqp/lib blib/snake.moarvm "$@"
34 changes: 17 additions & 17 deletions src/Snake/Actions.nqp
Expand Up @@ -29,23 +29,18 @@ method string($/) { make $<quote_EXPR>.ast; }
#method infix:sym<==>($/) { ... }
#method infix:sym<!=>($/) { ... }

method INDENT($/) { nqp::unshift_i(@*INDENT, $<sports>.ast); }

method DEDENT($/) {
my $new := $<EOF> ?? 0 !! $<sports>.ast;
nqp::shift_i(@*INDENT) while $new < @*INDENT[0];
nqp::die("Bad dedent: saw $new but expected @*INDENT[0]") if $new != @*INDENT[0];
}
method sports($/) {
my $indent := 0;
$indent := $indent + nqp::chars(~$/[0]);
if ~$/[1] {
$indent := $indent + (8 - $indent % 8); # Increment to nearest multiple of 8
$indent := $indent + 8*(nqp::chars(~$/[1])-1);
if $<EOF> { make 0 }
else {
my $indent := 0;
$indent := $indent + nqp::chars(~$/[0]);
if ~$/[1] {
$indent := $indent + (8 - $indent % 8); # Increment to nearest multiple of 8
$indent := $indent + 8*(nqp::chars(~$/[1])-1);
}

make $indent;
}
make $indent;
}

# 6: Expressions
Expand All @@ -67,7 +62,9 @@ method simple-statement:sym<expr>($/) { make $<EXPR>.ast; }

# 8: Compound statements
method compound-statement:sym<if>($/) {
make QAST::Op.new(:op<if>, $<EXPR>.ast, $<suite>.ast);
my $ast := QAST::Op.new(:op<if>, $<EXPR>.ast, $<suite>[0].ast);
$ast.push($<suite>[1].ast) if +$<suite> > 1;
make $ast;
}

method suite:sym<runon>($/) { make $<stmt-list>.ast; }
Expand Down Expand Up @@ -99,7 +96,10 @@ method file-input($/) {
$stmts.push($line.ast) if $line.ast;
}

make QAST::Block.new($stmts);
# XXX: This has been cargo-culted from Rakudo. Should probably figure out
# how this API should be used (and what it can do).
make QAST::CompUnit.new(QAST::Block.new(QAST::Var.new(:name<__args__>,
:scope<local>, :decl<param>, :slurpy(1)), $stmts), :hll<snake>);
}

method line($/) { make $<statement>.ast if $<statement>; }
Expand Down
72 changes: 58 additions & 14 deletions src/Snake/Grammar.nqp
Expand Up @@ -68,7 +68,7 @@ token keyword:sym<raise> { <sym> }
token string {
# TODO: u, r, b, triple-quotes, pretty much all of 2.4.1.
# TODO: String literal concatentation (2.4.2).
<?["]> <quote_EXPR: ':q'>
<?['"]> <quote_EXPR: ':q'>
}

### 2.4.4: Integer literals
Expand Down Expand Up @@ -111,29 +111,64 @@ token infix:sym<!=> { <sym> }
## 2.6: Delimiters
# Handled elsewhere, since we don't have a separate lexer stage.

token INDENT {
token INDENT($indent = -1) {
# Gobble up leading whitespace, push new indent onto stack (or die if bad
# indent).
#<!> # TODO
^^ <sports> <?{ $<sports>.ast > @*INDENT[0] }> <.MARKER: 'INDENT'> || <.panic: "Dedent not at beginning of line!">
<sports> <?{ self.check-indent($<sports>.ast, $indent) }>
}

token DEDENT {
# Gobble up leading whitespace, pop until we're done (or die if bad
# indent).
[^^ <sports> <?{ $<sports>.ast < @*INDENT[0] }> <.MARKER: 'INDENT'> | $<EOF>=<?> $] || <.panic: "Indent not at beginning of line!">
method check-indent(int $sports, int $indent) {
if $indent < 0 {
if $sports > nqp::atpos_i(@*INDENT, 0) {
nqp::unshift_i(@*INDENT, $sports);
1 == 1;
}
else {
1 == 0;
}
}
else {
$sports == $indent
}
}

#token DEDENT {
# <?before [^^ <sports> [<?{ self.check-dedent($<sports>.ast) }> || <.panic: "Dedent not consistent with any previous indent level">] | $<EOF>=<?> $]
# || <.panic: "Dedent not at beginning of line">>
#}

# This is a really hacky implementation of DEDENT. Really we just want the
# version above. However, it looks like $/ isn't available inside a <?before>,
# so we do it this way and implement the zero-width match ourself. The reason
# this works is of course that match will always match successfully. If it
# fails (that is, we're trying to match a dedent somewhere other than
# beginning of line), it'll panic and throw an exception.
method DEDENT() {
my $dedent := self.match-dedent;
self;
}

token match-dedent {
<sports> [<?{ self.check-dedent($<sports>.ast) }> || <.panic: "Dedent not consistent with any previous indent level">] | $<EOF>=<?> $
}

token check-indent {
<.MARKED: 'INDENT'> || ^^ <sports> <?{ $<sports>.ast == @*INDENT[0] }>
method check-dedent($sports) {
# Pop indents until we find the level we've indented back to.
while $sports < nqp::atpos_i(@*INDENT, 0) { nqp::shift_i(@*INDENT) }
$sports == nqp::atpos_i(@*INDENT, 0);
}

# Spaces or tabs. A valid Python indent consists of any number of spaces, then
# any number of tabs. If spaces are used after a tab, the indent is ambiguous
# and must be rejected (2.1.8: "Indentation is rejected as inconsistent if a
# source file mixes tabs and spaces in a way that makes the meaning dependent
# on the worth of a tab in spaces").
token sports { \f? (' '*) (\t*) [<[\ \f]> <.panic: "Ambiguous indentation">]? }
token sports {
[ | ^^ \f? (' '*) (\t*) [<[\ \f]> <.panic: "Ambiguous indentation">]?
| $<EOF>=<?> $
]
|| <.panic: "Indent not at beginning of line">
}

# 6: Expressions
## 6.2: Atoms
Expand All @@ -156,13 +191,22 @@ token simple-statement:sym<expr> { <EXPR> }

# 8: Compound statements
proto token compound-statement {*}
rule compound-statement:sym<if> { <sym> <EXPR> ':' <suite> }
rule compound-statement:sym<if> {
:my int $indent := nqp::atpos_i(@*INDENT, 0);
<sym> <EXPR> ':' <suite>
[<.INDENT: $indent> 'else' ':' <suite>]?
}

proto token suite {*}
token suite:sym<runon> { <stmt-list> <.NEWLINE> }
token suite:sym<normal> { <.NEWLINE> <.INDENT> <statement>+ <.DEDENT> }
token suite:sym<normal> {
<.NEWLINE>
<.INDENT> <statement>
[<.INDENT: nqp::atpos_i(@*INDENT, 0)> <statement>]*
<.DEDENT>
}

token statement { <.check-indent> [$<stmt>=<stmt-list> <.NEWLINE> | $<stmt>=<compound-statement>] }
token statement { $<stmt>=<stmt-list> <.NEWLINE> | $<stmt>=<compound-statement> }

token stmt-list { <simple-statement>+ %% [<.ws> ';' <.ws>] }

Expand Down
4 changes: 2 additions & 2 deletions src/snake.nqp
Expand Up @@ -9,8 +9,8 @@ $comp.language('snake');
$comp.parsegrammar(Snake::Grammar);
$comp.parseactions(Snake::Actions);

sub MAIN(*@args) {
$comp.command_line(@args[0], :encoding<utf8>);
sub MAIN(@args) {
$comp.command_line(@args, :encoding<utf8>);
}

# vim: ft=perl6
8 changes: 8 additions & 0 deletions t/sanity/if.t
@@ -0,0 +1,8 @@
if 1:
nqp::say('1..1')
if 0:
nqp::say("BAIL OUT!")
else:
nqp::print('not ')

nqp::say('ok 1 - else attachment')

0 comments on commit 2f600b0

Please sign in to comment.