# Copyright (C) 2010-2012, Parrot Foundation.
=head1 NAME
TAP/Parser
=head1 DESCRIPTION
Simplified port of TAP::Parser (version 3.21)
See L
=head3 Class TAP;Parser;Result
Base class for TAP::Parser output objects
=over 4
=cut
.namespace ['TAP';'Parser';'Result']
.sub '' :init :load :anon
$P0 = newclass ['TAP';'Parser';'Result']
$P0.'add_attribute'('raw')
$P0.'add_attribute'('directive')
$P0.'add_attribute'('explanation')
.end
=item get_string
=cut
.sub 'get_string' :vtable :method
$P0 = getattribute self, 'raw'
$S0 = $P0
.return ($S0)
.end
=item type
=cut
.sub 'type' :method
$S0 = typeof self
$P0 = split ';', $S0
$S0 = pop $P0
$S0 = downcase $S0
.return ($S0)
.end
=item has_todo
=cut
.sub 'has_todo' :method
$P0 = getattribute self, 'directive'
unless null $P0 goto L1
.return (0)
L1:
$S0 = $P0
$I0 = $S0 == 'TODO'
.return ($I0)
.end
=item has_skip
=cut
.sub 'has_skip' :method
$P0 = getattribute self, 'directive'
unless null $P0 goto L1
.return (0)
L1:
$S0 = $P0
$I0 = $S0 == 'SKIP'
.return ($I0)
.end
=item has_directive
=cut
.sub 'has_directive' :method
$I0 = self.'has_todo'()
if $I0 goto L1
$I0 = self.'has_skip'()
L1:
.return ($I0)
.end
=back
=head3 Class TAP;Parser;Result;Bailout
=over 4
=cut
.namespace ['TAP';'Parser';'Result';'Bailout']
.sub '' :init :load :anon
$P0 = subclass ['TAP';'Parser';'Result'], ['TAP';'Parser';'Result';'Bailout']
.end
=item bailout
=cut
.sub 'bailout' :method
$P0 = getattribute self, 'explanation'
.return ($P0)
.end
=back
=head3 Class TAP;Parser;Result;Comment
=cut
.namespace ['TAP';'Parser';'Result';'Comment']
.sub '' :init :load :anon
$P0 = subclass ['TAP';'Parser';'Result'], ['TAP';'Parser';'Result';'Comment']
$P0.'add_attribute'('comment')
.end
=head3 Class TAP;Parser;Result;Plan
=cut
.namespace ['TAP';'Parser';'Result';'Plan']
.sub '' :init :load :anon
$P0 = subclass ['TAP';'Parser';'Result'], ['TAP';'Parser';'Result';'Plan']
$P0.'add_attribute'('plan')
$P0.'add_attribute'('tests_planned')
.end
=head3 Class TAP;Parser;Result;Test
=over 4
=cut
.namespace ['TAP';'Parser';'Result';'Test']
.sub '' :init :load :anon
$P0 = subclass ['TAP';'Parser';'Result'], ['TAP';'Parser';'Result';'Test']
$P0.'add_attribute'('ok')
$P0.'add_attribute'('test_num')
$P0.'add_attribute'('description')
$P0.'add_attribute'('unplanned')
.end
=item get_string
=cut
.sub 'get_string' :vtable :method
$P0 = getattribute self, 'ok'
$S0 = $P0
$S0 .= ' '
$P0 = getattribute self, 'test_num'
$S1 = $P0
$S0 .= $S1
$P0 = getattribute self, 'description'
if null $P0 goto L1
$S1 = $P0
if $S1 == '' goto L1
$S0 .= ' '
$S0 .= $S1
L1:
$P0 = getattribute self, 'directive'
if null $P0 goto L2
$S1 = $P0
if $S1 == '' goto L2
$S0 .= ' # '
$S0 .= $S1
$S0 .= ' '
$P0 = getattribute self, 'explanation'
if null $P0 goto L2
$S1 = $P0
$S0 .= $S1
L2:
.return ($S0)
.end
=item is_ok
=cut
.sub 'is_ok' :method
$P0 = getattribute self, 'unplanned'
if null $P0 goto L1
unless $P0 goto L1
.return (0)
L1:
$I0 = self.'has_todo'()
if $I0 goto L2
$I0 = self.'is_actual_ok'()
L2:
.return ($I0)
.end
=item is_actual_ok
=cut
.sub 'is_actual_ok' :method
$P0 = getattribute self, 'ok'
$S0 = $P0
$I0 = index $S0, 'not'
$I0 = $I0 != 0
.return ($I0)
.end
=item todo_passed
=cut
.sub 'todo_passed' :method
$I0 = self.'has_todo'()
unless $I0 goto L1
$I0 = self.'is_actual_ok'()
L1:
.return ($I0)
.end
=item is_unplanned
=cut
.sub 'is_unplanned' :method
$I0 = 0
$P0 = getattribute self, 'unplanned'
if null $P0 goto L1
$I0 = $P0
L1:
.return ($I0)
.end
=back
=head3 Class TAP;Parser;Result;Unknown
=cut
.namespace ['TAP';'Parser';'Result';'Unknown']
.sub '' :init :load :anon
$P0 = subclass ['TAP';'Parser';'Result'], ['TAP';'Parser';'Result';'Unknown']
.end
=head3 Class TAP;Parser;Result;Version
=cut
.namespace ['TAP';'Parser';'Result';'Version']
.sub '' :init :load :anon
$P0 = subclass ['TAP';'Parser';'Result'], ['TAP';'Parser';'Result';'Version']
$P0.'add_attribute'('version')
.end
=head3 Class TAP;Parser;Grammar
C tokenizes lines and constructs C
subclasses to represent the tokens.
=over 4
=cut
.namespace ['TAP';'Parser';'Grammar']
.sub '' :init :load :anon
$P0 = newclass ['TAP';'Parser';'Grammar']
.end
.include 'cclass.pasm'
.sub 'trim' :anon
.param string str
$I0 = length str
$I0 = find_not_cclass .CCLASS_WHITESPACE, str, 0, $I0
str = substr str, $I0
$I0 = length str
L1:
dec $I0
unless $I0 > 0 goto L2
$I1 = is_cclass .CCLASS_WHITESPACE, str, $I0
if $I1 != 0 goto L1
L2:
inc $I0
str = substr str, 0, $I0
.return (str)
.end
=item tokenize
=cut
.sub 'tokenize' :method
.param string line
.local pmc result
.local int pos, lastpos
.local string up, directive, explanation, ok
lastpos = length line
$I0 = index line, 'not ok'
unless $I0 == 0 goto L1
ok = 'not ok'
pos = 6
goto L2
L1:
$I0 = index line, 'ok'
unless $I0 == 0 goto L3
ok = 'ok'
pos = 2
L2:
.local string description, test_num
test_num = ''
description = ''
directive = ''
explanation = ''
if pos == lastpos goto L4
$I0 = is_cclass .CCLASS_WHITESPACE, line, pos
unless $I0 goto L3
pos = find_not_cclass .CCLASS_WHITESPACE, line, pos, lastpos
if pos == lastpos goto L4
$I0 = is_cclass .CCLASS_NUMERIC, line, pos
unless $I0 goto L5
$I2 = find_not_cclass .CCLASS_NUMERIC, line, pos, lastpos
if $I2 == lastpos goto L_5
$I0 = is_cclass .CCLASS_WHITESPACE, line, $I2
unless $I0 goto L5
L_5:
$I1 = $I2 - pos
test_num = substr line, pos, $I1
if $I2 == lastpos goto L4
pos = find_not_cclass .CCLASS_WHITESPACE, line, $I2, lastpos
L5:
$S0 = substr line, pos
description = trim($S0)
up = upcase description
lastpos = length description
pos = -1
L6:
inc pos
pos = index description, '#', pos
if pos < 0 goto L4
$I1 = pos - 1
$S0 = substr description, $I1, 1
inc pos
if $S0 == "\\" goto L6
if pos == lastpos goto L4
$I0 = is_cclass .CCLASS_WHITESPACE, description, pos
unless $I0 != 0 goto L7
pos = find_not_cclass .CCLASS_WHITESPACE, description, pos, lastpos
if pos == lastpos goto L4
L7:
$S0 = substr description, pos, 4
$I0 = index up, 'SKIP', pos
if $I0 == pos goto L8
$I0 = index up, 'TODO', pos
if $I0 == pos goto L8
goto L6
L8:
directive = substr up, pos, 4
$I1 = pos + 4
$S0 = substr description, $I1
explanation = trim($S0)
pos -= 2
$S0 = substr description, 0, pos
description = trim($S0)
L4:
result = new ['TAP';'Parser';'Result';'Test']
$P1 = box line
setattribute result, 'raw', $P1
$P1 = box ok
setattribute result, 'ok', $P1
unless test_num != '' goto L11
$I0 = test_num
$P1 = box test_num
setattribute result, 'test_num', $P1
L11:
unless description != '' goto L12
$P1 = box description
setattribute result, 'description', $P1
L12:
unless directive != '' goto L13
$P1 = box directive
setattribute result, 'directive', $P1
L13:
unless explanation != '' goto L14
$P1 = box explanation
setattribute result, 'explanation', $P1
L14:
.return (result)
L3:
$I0 = index line, '#'
unless $I0 == 0 goto L19
result = new ['TAP';'Parser';'Result';'Comment']
$P1 = box line
setattribute result, 'raw', $P1
$S0 = substr line, 1
$S0 = trim($S0)
$P1 = box $S0
setattribute result, 'comment', $P1
.return (result)
L19:
$I0 = index line, '1..'
unless $I0 == 0 goto L21
pos = 3
$I0 = is_cclass .CCLASS_NUMERIC, line, pos
unless $I0 goto L21
pos = find_not_cclass .CCLASS_NUMERIC, line, pos, lastpos
directive = ''
explanation = ''
.local string plan
plan = substr line, 0, pos
.local int tests_planned
$I1 = pos - 3
$S0 = substr line, 3, $I1
tests_planned = $S0
if pos == lastpos goto L22
$I0 = is_cclass .CCLASS_WHITESPACE, line, pos
unless $I0 goto L23
pos = find_not_cclass .CCLASS_WHITESPACE, line, pos, lastpos
if pos == lastpos goto L22
L23:
$S0 = substr line, pos, 1
unless $S0 == '#' goto L21
inc pos
$I0 = is_cclass .CCLASS_WHITESPACE, line, pos
unless $I0 goto L24
pos = find_not_cclass .CCLASS_WHITESPACE, line, pos, lastpos
L24:
up = upcase line
$I0 = index up, 'SKIP', pos
unless $I0 == pos goto L21
directive = 'SKIP'
pos += 4
$S0 = substr line, pos
explanation = trim($S0)
L22:
result = new ['TAP';'Parser';'Result';'Plan']
$P1 = box line
setattribute result, 'raw', $P1
$P1 = box plan
setattribute result, 'plan', $P1
$P1 = box tests_planned
setattribute result, 'tests_planned', $P1
unless tests_planned == 0 goto L25
directive = 'SKIP'
L25:
unless directive != '' goto L26
$P1 = box directive
setattribute result, 'directive', $P1
L26:
unless explanation != '' goto L27
$P1 = box explanation
setattribute result, 'explanation', $P1
L27:
.return (result)
L21:
$I0 = index line, 'Bail out!'
unless $I0 == 0 goto L31
result = new ['TAP';'Parser';'Result';'Bailout']
$P1 = box line
setattribute result, 'raw', $P1
$S0 = substr line, 9
$S0 = trim($S0)
$P1 = box $S0
setattribute result, 'explanation', $P1
.return (result)
L31:
$I0 = index line, 'TAP'
unless $I0 == 0 goto L41
pos = 3
$I0 = is_cclass .CCLASS_WHITESPACE, line, pos
unless $I0 goto L41
pos = find_not_cclass .CCLASS_WHITESPACE, line, pos, lastpos
$I0 = index line, 'version', pos
unless $I0 == pos goto L41
pos += 7
$I0 = is_cclass .CCLASS_WHITESPACE, line, pos
unless $I0 goto L41
pos = find_not_cclass .CCLASS_WHITESPACE, line, pos, lastpos
$I0 = is_cclass .CCLASS_NUMERIC, line, pos
unless $I0 goto L41
$I2 = find_not_cclass .CCLASS_NUMERIC, line, pos, lastpos
$I1 = $I2 - pos
$S0 = substr line, pos, $I1
.local int version
version = $S0
if $I2 == lastpos goto L42
$I0 = is_cclass .CCLASS_WHITESPACE, line, $I2
unless $I0 goto L41
$I0 = find_not_cclass .CCLASS_WHITESPACE, line, $I2, lastpos
unless $I0 == lastpos goto L41
L42:
result = new ['TAP';'Parser';'Result';'Version']
$P1 = box line
setattribute result, 'raw', $P1
$P1 = box version
setattribute result, 'version', $P1
.return (result)
L41:
result = new ['TAP';'Parser';'Result';'Unknown']
$P1 = box line
setattribute result, 'raw', $P1
.return (result)
.end
=back
=head3 Class TAP;Base
Base class that provides common functionality to C
and C ie. callback support.
=over 4
=cut
.namespace ['TAP';'Base']
.sub '' :init :load :anon
$P0 = newclass ['TAP';'Base']
$P0.'add_attribute'('code_for')
$P0.'add_attribute'('ok_callbacks')
.end
=item callback
=cut
.sub 'callback' :method
.param string event
.param pmc callback
$P0 = getattribute self, 'ok_callbacks'
if null $P0 goto L1
$I0 = exists $P0[event]
if $I0 goto L1
$S0 = "Callback " . event
$S0 .= " is not supported."
die $S0
L1:
$P0 = getattribute self, 'code_for'
unless null $P0 goto L2
$P0 = new 'Hash'
setattribute self, 'code_for', $P0
L2:
$P0[event] = callback
.end
=item _has_callback
=cut
.sub '_has_callback' :method
$P0 = getattribute self, 'code_for'
if null $P0 goto L1
.return (1)
L1:
.return (0)
.end
=item _callback_for
=cut
.sub '_callback_for' :method
.param string event
null $P1
$P0 = getattribute self, 'code_for'
if null $P0 goto L1
$I0 = exists $P0[event]
unless $I0 goto L1
$P1 = $P0[event]
L1:
.return ($P1)
.end
=item _make_callback
=cut
.sub '_make_callback' :method
.param string event
.param pmc args :slurpy
$P0 = getattribute self, 'code_for'
if null $P0 goto L1
$I0 = exists $P0[event]
unless $I0 goto L1
$P0 = $P0[event]
$P0 = $P0(args :flat)
.return ($P0)
L1:
.return ()
.end
=back
=head3 Class TAP;Parser
C is designed to produce a proper parse of TAP output.
=over 4
=cut
.namespace ['TAP';'Parser']
.sub '' :init :load :anon
load_bytecode 'osutils.pbc'
$P0 = subclass ['TAP';'Base'], ['TAP';'Parser']
$P0.'add_attribute'('stream')
$P0.'add_attribute'('skipped')
$P0.'add_attribute'('todo')
$P0.'add_attribute'('passed')
$P0.'add_attribute'('failed')
$P0.'add_attribute'('actual_failed')
$P0.'add_attribute'('actual_passed')
$P0.'add_attribute'('todo_passed')
$P0.'add_attribute'('parse_errors')
$P0.'add_attribute'('tests_run')
$P0.'add_attribute'('tests_planned')
$P0.'add_attribute'('plan')
$P0.'add_attribute'('good_plan')
$P0.'add_attribute'('skip_all')
$P0.'add_attribute'('version')
$P0.'add_attribute'('exit')
$P0.'add_attribute'('ignore_exit')
$P0.'add_attribute'('merge')
$P0.'add_attribute'('spool')
$P0.'add_attribute'('start_time')
$P0.'add_attribute'('end_time')
$P0 = _make_state_table()
set_global ['TAP';'Parser'], 'STATES', $P0
$P0 = new 'Hash'
$P1 = split ' ', 'test version plan comment bailout unknown ALL ELSE EOF'
L1:
unless $P1 goto L2
$S0 = shift $P1
$P0[$S0] = 1
goto L1
L2:
set_global ['TAP';'Parser'], 'LEGAL_CALLBACK', $P0
.end
.sub 'init' :vtable :method
$P0 = new 'ResizableIntegerArray'
setattribute self, 'skipped', $P0
$P0 = new 'ResizableIntegerArray'
setattribute self, 'todo', $P0
$P0 = new 'ResizableIntegerArray'
setattribute self, 'passed', $P0
$P0 = new 'ResizableIntegerArray'
setattribute self, 'failed', $P0
$P0 = new 'ResizableIntegerArray'
setattribute self, 'actual_failed', $P0
$P0 = new 'ResizableIntegerArray'
setattribute self, 'actual_passed', $P0
$P0 = new 'ResizableIntegerArray'
setattribute self, 'todo_passed', $P0
$P0 = new 'ResizableStringArray'
setattribute self, 'parse_errors', $P0
$P0 = box 0
setattribute self, 'tests_run', $P0
$P0 = box 0
setattribute self, 'tests_planned', $P0
$P0 = get_global ['TAP';'Parser'], 'LEGAL_CALLBACK'
setattribute self, 'ok_callbacks', $P0
.end
=item start_time
=cut
.sub 'start_time' :method
$P0 = getattribute self, 'start_time'
.return ($P0)
.end
=item end_time
=cut
.sub 'end_time' :method
$P0 = getattribute self, 'end_time'
.return ($P0)
.end
=item skipped
=cut
.sub 'skipped' :method :nsentry
$P0 = getattribute self, 'skipped'
.return ($P0)
.end
=item todo
=cut
.sub 'todo' :method :nsentry
$P0 = getattribute self, 'todo'
.return ($P0)
.end
=item passed
=cut
.sub 'passed' :method :nsentry
$P0 = getattribute self, 'passed'
.return ($P0)
.end
=item failed
=cut
.sub 'failed' :method :nsentry
$P0 = getattribute self, 'failed'
.return ($P0)
.end
=item todo_passed
=cut
.sub 'todo_passed' :method :nsentry
$P0 = getattribute self, 'todo_passed'
.return ($P0)
.end
=item parse_errors
=cut
.sub 'parse_errors' :method :nsentry
$P0 = getattribute self, 'parse_errors'
.return ($P0)
.end
=item tests_run
=cut
.sub 'tests_run' :method :nsentry
$P0 = getattribute self, 'tests_run'
.return ($P0)
.end
=item tests_planned
=cut
.sub 'tests_planned' :method :nsentry
$P0 = getattribute self, 'tests_planned'
.return ($P0)
.end
=item merge
=cut
.sub 'merge' :method :nsentry
.param int val
$P0 = new 'Boolean'
set $P0, val
setattribute self, 'merge', $P0
.end
=item ignore_exit
=cut
.sub 'ignore_exit' :method :nsentry
.param int val
$P0 = new 'Boolean'
set $P0, val
setattribute self, 'ignore_exit', $P0
.end
=item exit
=cut
.sub 'exit' :method :nsentry
$P0 = getattribute self, 'ignore_exit'
if null $P0 goto L1
unless $P0 goto L1
.return (0)
L1:
$P0 = getattribute self, 'exit'
$I0 = 0
if null $P0 goto L2
$I0 = $P0
L2:
.return ($I0)
.end
=item has_problems
=cut
.sub 'has_problems' :method
$P0 = getattribute self, 'failed'
$I0 = elements $P0
if $I0 goto L1
$P0 = getattribute self, 'parse_errors'
$I0 = elements $P0
if $I0 goto L1
$P0 = getattribute self, 'ignore_exit'
if null $P0 goto L2
if $P0 goto L1
L2:
$P0 = getattribute self, 'exit'
if null $P0 goto L1
$I0 = $P0
.return ($I0)
L1:
.return ($I0)
.end
=item _add_error
=cut
.sub '_add_error' :method
.param pmc args :slurpy
$P0 = getattribute self, 'parse_errors'
$S0 = join '', args
$P1 = box $S0
push $P0, $P1
.end
=item is_good_plan
=cut
.sub 'is_good_plan' :method
$P0 = getattribute self, 'good_plan'
.return ($P0)
.end
=item spool
=cut
.sub 'spool' :method
.param pmc spool
setattribute self, 'spool', spool
.end
=item delete_spool
=cut
.sub 'delete_spool' :method
$P0 = getattribute self, 'spool'
null $P1
setattribute self, 'spool', $P1
.return ($P0)
.end
=item pragma
=cut
.sub 'pragma' :method
.param string name
.return (1)
.end
=item tap
=cut
.sub 'tap' :method
.param string tap
$P0 = new 'StringHandle'
$P0.'open'('tap', 'w')
$P0.'encoding'('utf8')
print $P0, tap
setattribute self, 'stream', $P0
.end
=item file
=cut
.sub 'file' :method
.param string filename
$P0 = new 'FileHandle'
push_eh _handler
$P0.'open'(filename, 'r')
pop_eh
$P0.'encoding'('utf8')
$S0 = $P0.'readline'()
$I0 = index $S0, '#!'
unless $I0 == 0 goto L1
$P0.'close'()
$S0 = _get_exec($S0)
.tailcall self.'exec'($S0, filename)
L1:
$P0.'seek'(0, 0)
setattribute self, 'stream', $P0
.return ()
_handler:
.local pmc ex
.get_results (ex)
$S0 = "Can't open '"
$S0 .= filename
$S0 .= "' ("
$S1 = err
$S0 .= $S1
$S0 .= ")\n"
ex = $S0
rethrow ex
.end
.include 'iglobals.pasm'
.sub '_get_exec' :anon
.param string line
$S0 = chomp(line)
$I0 = length $S0
$I0 = find_not_cclass .CCLASS_WHITESPACE, $S0, 2, $I0
$S0 = substr $S0, $I0
.local string slash
$P0 = getinterp
$P1 = $P0[.IGLOBALS_CONFIG_HASH]
slash = $P1['slash']
$P0 = split "/", $S0
$S0 = join slash, $P0
.return ($S0)
.end
=item exec
=cut
.sub 'exec' :method
.param pmc cmds :slurpy
.local string cmd
cmd = join ' ', cmds
$P0 = getattribute self, 'merge'
if null $P0 goto L1
unless $P0 goto L1
cmd .= ' 2>&1'
L1:
$P0 = new 'FileHandle'
push_eh _handler
$P0.'open'(cmd, 'pr')
pop_eh
$P0.'encoding'('utf8')
setattribute self, 'stream', $P0
.return ()
_handler:
.local pmc ex
.get_results (ex)
$S0 = "Can't open '"
$S0 .= cmd
$S0 .= "' ("
$S1 = err
$S0 .= $S1
$S0 .= ")\n"
ex = $S0
rethrow ex
.end
=item run
=cut
.sub 'run' :method
.const 'Sub' $P0 = 'next'
$P0 = newclosure $P0
L1:
$P1 = $P0(self)
unless null $P1 goto L1
.end
=item next
=cut
.sub 'next' :method :nsentry :lex
.local pmc stream, spool
stream = getattribute self, 'stream'
if null stream goto L1
$N0 = time
$P0 = box $N0
setattribute self, 'start_time', $P0
.local pmc grammar, st
grammar = new ['TAP';'Parser';'Grammar']
.const 'Sub' $P0 = 'next_state'
capture_lex $P0
st = box 'INIT'
.lex 'state', st
L2:
$S0 = stream.'readline'()
if $S0 == '' goto L3
$S0 = chomp($S0)
.local pmc token
token = grammar.'tokenize'($S0)
self.'next_state'(token)
$S0 = token.'type'()
$P0 = self.'_callback_for'($S0)
if null $P0 goto L4
$P0(token)
goto L5
L4:
self.'_make_callback'('ELSE', token)
L5:
self.'_make_callback'('ALL', token)
spool = getattribute self, 'spool'
if null spool goto L6
$S0 = token
print spool, $S0
print spool, "\n"
L6:
.yield (token)
goto L2
L3:
stream.'close'()
$I0 = can stream, 'exit_status'
unless $I0 goto L7
$I0 = stream.'exit_status'()
unless $I0 goto L7
$P0 = box $I0
setattribute self, 'exit', $P0
L7:
self.'_finish'()
$I0 = self.'_make_callback'('EOF', self)
null $P0
.return ($P0)
L1:
die "no stream"
.end
.sub 'next_state' :method :lex :outer('next')
.param pmc token
.local pmc STATES, st
STATES = get_global ['TAP';'Parser'], 'STATES'
st = find_lex 'state'
.local string type
type = token.'type'()
REDO:
$I0 = STATES[st]
if $I0 goto L1
$S0 = st
$S0 = "Illegal state: " . $S0
die $S0
L1:
$P0 = STATES[st]
$I0 = exists $P0[type]
unless $I0 goto L2
$P1 = $P0[type]
$I0 = exists $P1['act']
unless $I0 goto L3
$P2 = $P1['act']
$P2(self, token)
L3:
$I0 = exists $P1['continue']
unless $I0 goto L4
$S0 = $P1['continue']
set st, $S0
goto REDO
L4:
$I0 = exists $P1['goto']
unless $I0 goto L5
$S0 = $P1['goto']
set st, $S0
goto L5
L2:
$P0 = getinterp
$P1 = $P0.'stderr_handle'()
$P1.'print'("Unhandled token type: ")
$P1.'print'(type)
$P1.'print'("\n")
L5:
.end
.sub '_make_state_table'
.local pmc states
states = new 'Hash'
$P0 = split ' ', 'INIT PLAN PLANNED PLANNED_AFTER_TEST GOT_PLAN UNPLANNED UNPLANNED_AFTER_TEST'
$P1 = split ' ', 'bailout comment plan test unknown version'
L1:
unless $P0 goto L2
$S0 = shift $P0
.local pmc st
st = new 'Hash'
states[$S0] = st
$P2 = iter $P1
L3:
unless $P2 goto L4
$S0 = shift $P2
$P3 = new 'Hash'
st[$S0] = $P3
goto L3
L4:
$P3 = st['version']
$P4 = get_hll_global ['TAP';'Parser'], '_DEFAULT_version'
$P3['act'] = $P4
$P3 = st['unknown']
$P4 = get_hll_global ['TAP';'Parser'], '_DEFAULT_unknown'
$P3['act'] = $P4
$P3 = st['plan']
$P4 = get_hll_global ['TAP';'Parser'], '_DEFAULT_plan'
$P3['act'] = $P4
$P3 = st['test']
$P4 = get_hll_global ['TAP';'Parser'], '_DEFAULT_test'
$P3['act'] = $P4
goto L1
L2:
st = states['INIT']
$P0 = st['version']
$P1 = get_hll_global ['TAP';'Parser'], '_INIT_version'
$P0['act'] = $P1
$P0['goto'] = 'PLAN'
$P0 = st['plan']
$P0['goto'] = 'PLANNED'
$P0 = st['test']
$P0['goto'] = 'UNPLANNED'
st = states['PLAN']
$P0 = st['plan']
$P0['goto'] = 'PLANNED'
$P0 = st['test']
$P0['goto'] = 'UNPLANNED'
st = states['PLANNED']
$P0 = st['test']
$P0['goto'] = 'PLANNED_AFTER_TEST'
$P0 = st['plan']
$P1 = get_hll_global ['TAP';'Parser'], '_PLANNED_plan'
$P0['act'] = $P1
st = states['PLANNED_AFTER_TEST']
$P0 = st['test']
$P0['goto'] = 'PLANNED_AFTER_TEST'
$P0 = st['plan']
$P1 = get_hll_global ['TAP';'Parser'], '_no_action'
$P0['act'] = $P1
$P0['continue'] = 'PLANNED'
st = states['GOT_PLAN']
$P0 = st['test']
$P1 = get_hll_global ['TAP';'Parser'], '_GOT_PLAN_test'
$P0['act'] = $P1
$P0['continue'] = 'PLANNED'
$P0 = st['plan']
$P0['continue'] = 'PLANNED'
st = states['UNPLANNED']
$P0 = st['test']
$P0['goto'] = 'UNPLANNED_AFTER_TEST'
$P0 = st['plan']
$P0['goto'] = 'GOT_PLAN'
st = states['UNPLANNED_AFTER_TEST']
$P0 = st['test']
$P1 = get_hll_global ['TAP';'Parser'], '_no_action'
$P0['act'] = $P1
$P0['continue'] = 'UNPLANNED'
$P0 = st['plan']
$P1 = get_hll_global ['TAP';'Parser'], '_no_action'
$P0['act'] = $P1
$P0['continue'] = 'UNPLANNED'
.return (states)
.end
.sub '_no_action' :method :nsentry
.param pmc result
# nothing
.end
.sub '_DEFAULT_version' :method :nsentry
.param pmc result
self.'_add_error'('If TAP version is present it must be the first line of output')
.end
.sub '_DEFAULT_unknown' :method :nsentry
.param pmc result
$I0 = self.'pragma'('strict')
unless $I0 goto L1
$P0 = getattribute result, 'raw'
$S1 = $P0
self.'_add_error'('Unknown TAP token: "', $S1, '"')
L1:
.end
.sub '_DEFAULT_plan' :method :nsentry
.param pmc result
$P0 = getattribute result, 'tests_planned'
setattribute self, 'tests_planned', $P0
$P0 = getattribute result, 'plan'
setattribute self, 'plan', $P0
$I0 = result.'has_skip'()
unless $I0 goto L1
$S0 = '(no reason given)'
$P0 = getattribute result, 'explanation'
if null $P0 goto L2
$S0 = $P0
L2:
$P0 = box $S0
setattribute self, 'skip_all', $P0
L1:
.end
.sub '_DEFAULT_test' :method :nsentry
.param pmc result
$P0 = getattribute self, 'tests_run'
inc $P0
.local int tests_run
tests_run = $P0
$P0 = getattribute self, 'tests_planned'
.local int tests_planned
tests_planned = $P0
unless tests_planned goto L11
unless tests_run > tests_planned goto L11
$P0 = new 'Boolean'
set $P0, 1
setattribute result, 'unplanned', $P0
L11:
$P0 = getattribute result, 'test_num'
if null $P0 goto L21
.local int number
number = $P0
unless number != tests_run goto L22
$S1 = number
$S2 = tests_run
self.'_add_error'("Tests out of sequence. Found (", $S1, ") but expected (", $S2, ")")
goto L22
L21:
number = tests_run
$P0 = box number
setattribute result, 'test_num', $P0
L22:
$I0 = result.'has_todo'()
unless $I0 goto L31
$P0 = getattribute self, 'todo'
push $P0, number
L31:
$I0 = result.'todo_passed'()
unless $I0 goto L32
$P0 = getattribute self, 'todo_passed'
push $P0, number
L32:
$I0 = result.'has_skip'()
unless $I0 goto L33
$P0 = getattribute self, 'skipped'
push $P0, number
L33:
$I0 = result.'is_ok'()
unless $I0 goto L34
$P0 = getattribute self, 'passed'
push $P0, number
goto L35
L34:
$P0 = getattribute self, 'failed'
push $P0, number
L35:
$I0 = result.'is_actual_ok'()
unless $I0 goto L36
$P0 = getattribute self, 'actual_passed'
push $P0, number
goto L37
L36:
$P0 = getattribute self, 'actual_failed'
push $P0, number
L37:
.end
.sub '_INIT_version' :method :nsentry
.param pmc result
$P0 = getattribute result, 'version'
setattribute self, 'version', $P0
.end
.sub '_PLANNED_plan' :method :nsentry
.param pmc result
self.'_add_error'('More than one plan found in TAP output')
.end
.sub '_GOT_PLAN_test' :method :nsentry
.param pmc result
$P0 = getattribute self, 'plan'
$S1 = $P0
self.'_add_error'("Plan (", $S1, ") must be at the beginning or end of the TAP output")
self.'is_good_plan'(0)
.end
.sub '_finish' :method
$N0 = time
$P0 = box $N0
setattribute self, 'end_time', $P0
$P0 = getattribute self, 'plan'
unless null $P0 goto L1
self.'_add_error'('No plan found in TAP output')
goto L2
L1:
$P0 = getattribute self, 'good_plan'
unless null $P0 goto L2
$P0 = box 1
setattribute self, 'good_plan', $P0
L2:
.local int tests_run, tests_planned
$P0 = getattribute self, 'tests_run'
tests_run = $P0
tests_planned = 0
$P0 = getattribute self, 'tests_planned'
if null $P0 goto L3
tests_planned = $P0
L3:
unless tests_run != tests_planned goto L4
$P0 = box 0
setattribute self, 'good_plan', $P0
if tests_planned == 0 goto L4
$S1 = tests_planned
$S2 = tests_run
self.'_add_error'("Bad plan. You planned ", $S1, " tests but ran ", $S2, ".")
L4:
$P0 = getattribute self, 'good_plan'
unless null $P0 goto L5
$P0 = box 0
setattribute self, 'good_plan', $P0
L5:
.end
=back
=head3 Class TAP;Parser;Aggregator
C collects parser objects and allows
reporting/querying their aggregate results.
=over 4
=cut
.namespace ['TAP';'Parser';'Aggregator']
.sub '' :init :load :anon
$P0 = newclass ['TAP';'Parser';'Aggregator']
$P0.'add_attribute'('parser_for')
$P0.'add_attribute'('parse_order')
$P0.'add_attribute'('start_time')
$P0.'add_attribute'('end_time')
$P0.'add_attribute'('failed')
$P0.'add_attribute'('parse_errors')
$P0.'add_attribute'('passed')
$P0.'add_attribute'('skipped')
$P0.'add_attribute'('todo')
$P0.'add_attribute'('todo_passed')
$P0.'add_attribute'('total')
$P0.'add_attribute'('planned')
$P0.'add_attribute'('exit')
$P0.'add_attribute'('description_for_failed')
$P0.'add_attribute'('description_for_parse_errors')
$P0.'add_attribute'('description_for_passed')
$P0.'add_attribute'('description_for_skipped')
$P0.'add_attribute'('description_for_todo')
$P0.'add_attribute'('description_for_todo_passed')
$P0.'add_attribute'('description_for_planned')
$P0.'add_attribute'('description_for_exit')
$P0 = new 'Hash'
$P1 = get_hll_global ['TAP';'Parser'], 'failed'
$P0['failed'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'parse_errors'
$P0['parse_errors'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'passed'
$P0['passed'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'skipped'
$P0['skipped'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'todo'
$P0['todo'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'todo_passed'
$P0['todo_passed'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'tests_run'
$P0['total'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'tests_planned'
$P0['planned'] = $P1
$P1 = get_hll_global ['TAP';'Parser'], 'exit'
$P0['exit'] = $P1
set_global ['TAP';'Parser';'Aggregator'], 'SUMMARY_METHOD_FOR', $P0
.end
.sub 'init' :vtable :method
$P0 = new 'Hash'
setattribute self, 'parser_for', $P0
$P0 = new 'ResizableStringArray'
setattribute self, 'parse_order', $P0
.local pmc SUMMARY_METHOD_FOR
SUMMARY_METHOD_FOR = get_global ['TAP';'Parser';'Aggregator'], 'SUMMARY_METHOD_FOR'
$P0 = iter SUMMARY_METHOD_FOR
L1:
unless $P0 goto L2
$S0 = shift $P0
$P1 = box 0
setattribute self, $S0, $P1
if $S0 == 'total' goto L1
$S0 = 'description_for_' . $S0
$P1 = new 'ResizableStringArray'
setattribute self, $S0, $P1
goto L1
L2:
.end
=item add
=cut
.sub 'add' :method
.param string description
.param pmc parser
.local pmc parser_for
parser_for = getattribute self, 'parser_for'
$I0 = exists parser_for[description]
unless $I0 goto L1
$S0 = "You already have a parser for (" . description
$S0 .= "). Perhaps you have run the same test twice.\n"
die $S0
L1:
parser_for[description] = parser
.local pmc parse_order
parse_order = getattribute self, 'parse_order'
push parse_order, description
.local pmc SUMMARY_METHOD_FOR
SUMMARY_METHOD_FOR = get_global ['TAP';'Parser';'Aggregator'], 'SUMMARY_METHOD_FOR'
$P0 = iter SUMMARY_METHOD_FOR
L2:
unless $P0 goto L3
.local string summary
summary = shift $P0
.local pmc method
method = SUMMARY_METHOD_FOR[summary]
.local int count
count = method(parser)
unless count > 0 goto L2
$P1 = getattribute self, summary
$P1 += count
if summary == 'total' goto L2
$S0 = 'description_for_' . summary
$P1 = getattribute self, $S0
push $P1, description
goto L2
L3:
.end
=item parsers
=cut
.sub 'parsers' :method
.param string desc
$P0 = getattribute self, 'parser_for'
$P1 = $P0[desc]
.return ($P1)
.end
=item total
=cut
.sub 'total' :method
$P0 = getattribute self, 'total'
$I0 = $P0
.return ($I0)
.end
=item passed
=cut
.sub 'passed' :method
$P0 = getattribute self, 'passed'
$I0 = $P0
.return ($I0)
.end
=item descriptions
=cut
.sub 'descriptions' :method
$P0 = getattribute self, 'parse_order'
.return ($P0)
.end
=item start
=cut
.sub 'start' :method
$N0 = time
$P0 = box $N0
setattribute self, 'start_time', $P0
.end
=item stop
=cut
.sub 'stop' :method
$N0 = time
$P0 = box $N0
setattribute self, 'end_time', $P0
.end
=item start_time
=cut
.sub 'start_time' :method
$P0= getattribute self, 'start_time'
.return ($P0)
.end
=item en_time
=cut
.sub 'end_time' :method
$P0= getattribute self, 'end_time'
.return ($P0)
.end
=item elapsed
=cut
.sub 'elapsed' :method
$P0 = getattribute self, 'end_time'
if null $P0 goto L1
$N2 = $P0
$P0 = getattribute self, 'start_time'
if null $P0 goto L1
$N1 = $P0
$N0 = $N2 - $N1
.return ($N0)
L1:
die "Can't call elapsed without first calling start and then stop"
.end
=item elapsed_timestr
=cut
.sub 'elapsed_timestr' :method
$N0 = self.'elapsed'()
$P0 = new 'FixedPMCArray'
$P0 = 1
$P0[0] = $N0
$S0 = sprintf "%.3f wallclock secs", $P0
.return ($S0)
.end
=item has_problems
=cut
.sub 'has_problems' :method
$P0 = getattribute self, 'todo_passed'
$I0 = $P0
if $I0 goto L1
$I0 = self.'has_errors'()
L1:
.return ($I0)
.end
=item has_errors
=cut
.sub 'has_errors' :method
$P0 = getattribute self, 'failed'
$I0 = $P0
if $I0 goto L1
$P0 = getattribute self, 'parse_errors'
$I0 = $P0
if $I0 goto L1
$P0 = getattribute self, 'exit'
$I0 = $P0
L1:
.return ($I0)
.end
=item get_status
=cut
.sub 'get_status' :method
.local int total, passed
$P0 = getattribute self, 'total'
total = $P0
$P0 = getattribute self, 'passed'
passed = $P0
$I0 = self.'has_errors'()
if $I0 goto L1
if total == passed goto L2
L1:
.return ('FAIL')
L2:
unless total goto L3
.return ('PASS')
L3:
.return ('NOTESTS')
.end
=item all_passed
=cut
.sub 'all_passed' :method
.local int total
$P0 = getattribute self, 'total'
total = $P0
unless total goto L1
$P0 = getattribute self, 'passed'
$I0 = $P0
unless total == $I0 goto L1
$I0 = self.'has_errors'()
$I0 = not $I0
.return ($I0)
L1:
.return (0)
.end
=back
=head1 AUTHOR
Francois Perrad
=cut
# Local Variables:
# mode: pir
# fill-column: 100
# End:
# vim: expandtab shiftwidth=4 ft=pir: