#! perl -MParse::RecDescent - slang.grammar Slang QUOTEDSTRING: /(?:(?:(?:"")|(?:".*?[^\\]"))|(?:(?:'')|(?:'.*?[^\\]')))/ { $item[1] =~ s/^"(.+)"$/$1/; $item[1] } game_definition: 'logic' '{' statement(s?) '}' 'display' '{' display_statement(s?) '}' { return 1; } statement: rule | /(local|const)?/ declaration { main::SetState( $item[1], $item[2] ); } | declaration: int_declaration | array_declaration # optimised for most common case: single integer int_declaration: 'int' identifier '{' intatom '}' { my ($h) = main::Eval( $item[4] ); if( defined($h) ) { $h = [ $item[2], $h ]; $h; } else { print STDERR " ** Failed to resolve expression.\n"; undef; } } | 'int' identifier '{' intexpression '}' { my ($h) = main::Eval( $item[5] ); if( defined($h) ) { $h = [ $item[3], $h ]; $h; } else { print STDERR " ** Failed to resolve expression.\n"; undef; } } | # optimised for most common case: sequence of integers array_declaration: 'array' identifier '{' intatom(s /,/) /,?/ '}' { my $error = 0; my $h = [ map { my ($r) = main::Eval($_); defined($r) or $error = 1; $r } @{$item[4]} ]; if( $error ) { print STDERR " ** Failed to resolve expression.\n"; undef; } else { $h = [ $item[2], $h ]; $h } } | 'array' identifier '{' intexpression(s /,/) /,?/ '}' { my $error = 0; my $h = [ map { my ($r) = main::Eval($_); defined($r) or $error = 1; $r } @{$item[5]} ]; if( $error ) { print STDERR " ** Failed to resolve expression.\n"; undef; } else { $h = [ $item[3], $h ]; $h } } | identifier: /[A-Za-z0-9_]+/ { if( main::IsReserved( $item[1] ) ) { undef; } else { $item[1]; } } rule: ruletype { main::AddRule( $item[1] ); } ruletype: 'for' identifier '(' inline_int_function '..' inline_int_function ')' '{' ruletype(s?) '}' { [ $item[3], @{$item[5]}, @{$item[7]}, [map { @{$_} } @{$item[10]}], main::Opcode( 'for' ) ] } | 'when' '{' boolexpression '}' allow_statement { [ @{$item[4]}, $item[6], main::Opcode( 'when' ) ] } | 'always' allow_statement { [ 1, [@{$item[3]}], main::Opcode( 'when' ) ] } | allow_statement: allow label { [[@{$item[1]}], [@{$item[2]}], main::Opcode('allow')] } | allow: 'allow' '{' assignment(s) '}' { [ map { @{$_} } @{$item[4]} ] } | label: 'label' '{' textitem(s) '}' { [ map { @{$_} } @{$item[4]} ] } | assignment: 'for' identifier '(' inline_int_function '..' inline_int_function ')' '{' assignment(s?) '}' { [ $item[3], @{$item[5]}, @{$item[7]}, [map { @{$_} } @{$item[10]}], main::Opcode( 'for' ) ] } | 'if' '(' boolexpression ')' '{' assignment(s?) '}' { [ @{$item[4]}, [map { @{$_} } @{$item[7]}], main::Opcode( 'if' ) ] } | inline_lvalue /[+-]?=/ inline_expression ';' { my $h = \('function(){ ' . $item[1] . ' ' . $item[2] . ' ' . $item[4] . ' }'); [ \$h ]; } | inline_optional_incdec inline_lvalue inline_optional_incdec ';' { my $h = \('function(){ ' . $item[1] . $item[2] . $item[3] . ' }'); [ \$h ]; } | textitem: QUOTEDSTRING { [ $item[1] ] } | inline_int_function { $item[1] } inline_textitem: QUOTEDSTRING { '"' . $item[1] . '"' } | inline_expression { '""+(' . $item[1] . ')' } boolexpression: inline_bool_function { $item[1] } intexpression: intproduct optionalintsum(s?) { [ @{$item[1]}, map { @{$_} } @{$item[2]} ] } optionalintsum: /[+-]/ intproduct { [ @{$item[3]}, main::Opcode($item[1] ) ] } intproduct: logicexpression optionalintmul(s?) { [ @{$item[1]}, map { @{$_} } @{$item[2]} ] } optionalintmul: /[*\/]/ logicexpression { [ @{$item[3]}, main::Opcode($item[1] ) ] } logicexpression: intatom optionallogic(s?) { [ @{$item[1]}, map { @{$_} } @{$item[2]} ] } optionallogic: /[%|&^]/ intatom { [ @{$item[3]}, main::Opcode($item[1] ) ] } intatom: /(?:\+|-)?[0-9]+/ { [ $item[1] ] } | '(' intexpression ')' { $item[3] } | lvalue { $item[1] } inline_bool_function: inline_bool_expression { my $h = \('function(_stack){ _stack.push(dataitem(' . $item[1] . ')); }'); [ \$h ]; } inline_bool_expression: inline_boolatom inline_optional_bool_tail(s?) { $item[1] . join('', @{$item[2]}) } inline_optional_bool_tail: 'and' inline_bool_expression { ' && ' . $item[3] } | 'or' inline_bool_expression { ' || ' . $item[3] } inline_boolatom: '!' inline_boolatom { '!' . $item[3] } | inline_expression optionalcomparison(?) { if( $#{$item[2]} == 0 ) { '(' . $item[1] . ')' . $item[2]->[0] ; } else { '((' . $item[1] . ')!=0)'; } } | '(' inline_bool_expression ')' { '(' . $item[2] . ')' } | 'true' { 1 } | 'false' { 0 } optionalcomparison: /(?:(?:[!=]=)|(?:[\<\>]=?))/ inline_expression { $item[1] . '(' . $item[3] . ')' } inline_int_function: inline_expression { my $h = \('function(_stack){ _stack.push(dataitem(' . $item[1] . ')); }'); [ \$h ]; } inline_expression: inline_possible_division optional_inline_sum(s?) { $item[1] . join('', @{$item[2]}) } optional_inline_sum: '+' inline_possible_division { ' - - ' . $item[3] } | '-' inline_possible_division { ' - ' . $item[3] } inline_possible_division: inline_product inline_division(?) { if($#{$item[2]} > -1) { 'Math.floor(' . $item[1] . $item[2]->[0] . ')'; } else { $item[1]; } } inline_division: '/' inline_possible_division { '/' . $item[3] } inline_product: inline_logic_expression optional_inline_mul(s?) { $item[1] . join('', @{$item[2]}) } optional_inline_mul: '*' inline_logic_expression { $item[1] . $item[3] } inline_logic_expression: inline_atom inline_optional_logic(s?) { $item[1] . join('', @{$item[2]}) } inline_optional_logic: /[%|&^]/ inline_atom { $item[1] . $item[3] } inline_atom: '(' inline_expression ')' { '(' . $item[3] . ')' } | /(?:\+|-)?[0-9]+/ { $item[1] } | 'random' optional_inline_array_dereference { if( $item[2] ne '' ) { 'Math.floor(Math.random() * ' . $item[2] . ')' } else { 'Math.floor(Math.random() * 2)' } } | inline_optional_incdec inline_lvalue inline_optional_incdec { $item[1] . $item[2] . $item[3] } inline_optional_incdec: '++' { $item[1] } | '--' { $item[1] } | { '' } # nothing inline_lvalue: identifier optional_inline_array_dereference { if( $item[2] ne '' ) { 'STATE[ "' . $item[1] . '" ][ ' . $item[2] . ' ].m_data'; } else { 'STATE[ "' . $item[1] . '" ].m_data'; } } lvalue: identifier optional_array_dereference { if( $item[2] ne '' ) { [ $item[1], @{$item[2]}, main::Opcode( 'array' ) ] } else { [ $item[1], main::Opcode( 'int' ) ] } } optional_array_dereference: '[' intexpression ']' { $item[3] } | { '' } optional_inline_array_dereference: '[' inline_expression ']' { $item[3] } | { '' } display_statement: display_declaration | display_imperative { main::AddDisplayCode( $item[1] ); } | repeatedly display_imperative: 'for' identifier '(' inline_int_function '..' inline_int_function ')' '{' display_imperative(s) '}' { [ $item[3], @{$item[5]}, @{$item[7]}, [map { @{$_} } @{$item[10]}], main::Opcode( 'for' ) ] } | 'if' '(' boolexpression ')' '{' display_imperative(s) '}' { [ @{$item[4]}, [map { @{$_} } @{$item[7]}], main::Opcode( 'if' ) ] } | element { $item[1] } | display_declaration: 'stringtable' identifier '{' textexpression(s /,/) /,?/ '}' { main::AddStringTable( $item[3], [map { @{$_} } @{$item[5]}] ); } textexpression: display_textitem(s) { [map { @{$_} } @{$item[1]}] } display_textitem: stringtable_lookup { $item[1] } | textitem { $item[1] } inline_textexpression: inline_display_textitem(s) { my $h = join('+', @{$item[1]}); $h; } inline_display_textitem: inline_stringtable_lookup { $item[1] } | inline_textitem { $item[1] } inline_stringtable_lookup: stringtable_identifier '[' inline_expression ']' { 'GetStringTableState( "' . $item[1] . '", ' . $item[4] . ' )'; } stringtable_lookup: stringtable_identifier '[' inline_int_function ']' { [ $item[1], @{$item[4]}, main::Opcode( 'stringtable' ) ] } stringtable_identifier: identifier { if( defined( main::GetStringtableString( $item[1] ) ) ) { $item[1]; } else { undef; } } element: box { $item[1] } | newline { $item[1] } | sprite { $item[1] } repeatedly: 'repeatedly' '{' QUOTEDSTRING(s /[,\s]/) /,?/ '}' { foreach my $action ( @{$item[4]} ) { main::AddRepeatedAction($action); } } newline: 'newline' { [ main::Opcode('newline') ] } box: 'box' '{' box_statement(s?) '}' { my $h = \( 'function(_stack)' . '{' . 'if( CurrentBox != undefined )' . '{' . join('', @{$item[4]}) . '}' . '}' ); [ [\$h], main::Opcode('box') ] } | sprite: 'sprite' '{' box_statement(s?) '}' { my $h = \( 'function(_stack)' . '{' . 'if( CurrentBox != undefined )' . '{' . join('', @{$item[4]}) . '}' . '}' ); [ [\$h], main::Opcode('sprite') ] } | box_statement: visual { $item[1] } | 'for' identifier '(' inline_expression '..' inline_expression ')' '{' box_statement(s?) '}' { my $h = ' for( var i = ' . $item[5] . '; i <= ' . $item[7] . ' ; i++ )' . ' {' . ' PushState( "' . $item[3] . '", i );' . join("; ", @{$item[10]} ) . ' PopState();' . ' }'; $h; } | 'if' '(' inline_bool_expression ')' '{' box_statement(s?) '}' { my $h = ' if( ' . $item[4] . ' )' . ' {' . join('', @{$item[7]} ) . ' }'; $h; } | binding { $item[1] } visual: format { $item[1] } | position { $item[1] } | content { $item[1] } binding: 'bind_action' '{' inline_textexpression(s) '}' { 'CurrentBox.m_bindings.unshift(' . join('+', @{$item[4]}) . ');'; } | 'while_active' '{' box_statement(s?) '}' { 'if( CurrentBoxHasBindings() ) {' . join( '', @{$item[4]} ) . '}'; } | format: /height|width|border|background|margin|padding/ '{' css(s?) '}' { 'CurrentBox.m_css["' . $item[1]. '"] = "' . (join(' ', @{$item[4]})) . '";' } | position: 'position' '{' css_position(s? /[\s;]/) /;?/ '}' { join('', @{$item[4]}); } | css_position: /absolute|relative|static|fixed/ { 'CurrentBox.m_css["position"] = "' . $item[1] . '";' } | 'left' ':' inline_expression /em|ex|px|in|cm|mm|pt|pc/ { 'CurrentBox.m_css["left"] = (' . $item[4] . ')+"' . $item[5] . '";' } | 'top' ':' inline_expression /em|ex|px|in|cm|mm|pt|pc/ { 'CurrentBox.m_css["top"] = (' . $item[4] . ')+"' . $item[5] . '";' } | css: css_size { $item[1] } | css_colour { $item[1] } | css_style { $item[1] } | css_inline_expression: inline_expression { if($item[1] =~ /^[+-]?[0-9]+$/) { $item[1]; } else { '"+' . $item[1] . '+"'; } } css_size: css_inline_expression /(em|ex|px|in|cm|mm|pt|pc)/ { $item[1] . $item[2] } css_style: /none|dotted|dashed|solid|double|groove|ridge|inset|outset/ { $item[1] } css_colour: /(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)|(\#(?:(?:[0-9A-Fa-f]{3})|(?:[0-9A-Fa-f]{6})))/ { $item[1] } | css_rgb_colour { $item[1] } css_rgb_colour: 'rgb' '(' inline_expression ',' inline_expression ',' inline_expression ')' { 'rgb("+'. $item[3] . '+","+' . $item[5] . '+","+' . $item[7] . '+") '; } css_percentage: inline_expression '%' { '"+' . $item[1].'+"%' } content: 'content' '{' inline_textexpression(s?) '}' { 'CurrentBox.m_content = HTML(' . join('+', @{$item[4]}) . ');'; }