import codecs
import re
import subprocess
import os.path as p
DEV_PARSER_FILE = './parser.rs'
PRELUDE_FILE = './prelude.rs'
INLINED_PARSER_FILE = './inlined_parser.rs'
INPUT_PEG_FILE = './examples/nailgun.peg'
COPYRIGHT_HEADER = """
// Copyright 2014 Strahinja Val Markovic
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
"""
def StripRules( contents ):
return re.sub( ur'// RULES START.*?// RULES END', u'', contents,
flags = re.DOTALL )
def StripComments( contents ):
return re.sub( ur'\s*(?<!/)//(?!(/|!)).*$',
u'',
contents,
flags = re.MULTILINE )
def StripCrateId( contents ):
return re.sub( ur'^#!\[crate_id.*?$\n',
u'',
contents,
flags = re.MULTILINE )
def StripTests( contents ):
while True:
match = re.search( ur'#\[cfg\(test\)\]\s*?( *)mod tests \{', contents,
re.DOTALL | re.MULTILINE )
if not match:
break
mod_indent = ( match.group( 1 ) or u'' ).strip( '\n' )
regex = re.compile( u'^' + mod_indent + '\}', re.MULTILINE )
brace = list( regex.finditer( contents, match.end() ) )[ 0 ]
contents = contents[ : match.start() ] + contents[ brace.end() : ]
return contents
def StripExtraWhitespace( contents ):
contents = re.sub( ur'\s*?^( *\})', '\n\\1', contents, flags = re.MULTILINE )
return re.sub( u'[\r \t]+$', '', contents, flags = re.MULTILINE )
def FindModuleFile( module_name, parent_file ):
dir_start = p.split( parent_file )[ 0 ]
module_dir = p.join( dir_start, module_name, 'mod.rs' )
if p.isfile( module_dir ):
return module_dir
module_file = p.join( dir_start, module_name + '.rs' )
if p.isfile( module_file ):
return module_file
raise ValueError( 'Module {} not found!'.format( module_name ) )
def FileContents( filename ):
return codecs.open( filename, 'r', 'utf-8' ).read().strip()
def InlineModules( filename, contents ):
while True:
match = re.search( ur'(\s*)(pub\s+)?mod (\w+);', contents )
if not match:
break
module_name = match.group( 3 )
module_file = FindModuleFile( module_name, filename )
module_contents = InlineModules( module_file,
FileContents( module_file ) )
current_indent = match.group( 1 ).strip( '\n' )
module_contents = re.sub( u'^',
current_indent + u' ',
module_contents,
flags = re.MULTILINE )
pub_prefix = match.group( 2 ) or u''
contents = ( contents[ : match.start( 0 ) ] + (
u'\n{0}{1}mod {2} {{\n{3}\n{0}}}'.format( current_indent,
pub_prefix,
module_name,
module_contents ) ) +
contents[ match.end( 0 ) : ] )
return contents
def PutPlaceholderForTopRule( contents ):
return contents.replace( u'match rules::Grammar(',
u'match rules::NGTOP_LEVEL_RULE(' )
def PreludeWrap( contents ):
return ''.join( [
COPYRIGHT_HEADER,
"""pub static PRELUDE : &'static str = r###"#![allow(dead_code)]\n""",
contents,
'"###;' ] )
def ExtractRules( inlined_parser ):
consume = False
rules = []
for line in inlined_parser.split( '\n' ):
if line.startswith( ' rule!( Grammar' ):
consume = True
if consume:
if line.strip():
rules.append( line )
else:
break
return '\n'.join( rules )
def ReplaceRules( parser, new_rules ):
match = re.search( ur'// RULES START\n\n(.*?)\s+// RULES END',
parser,
flags = re.DOTALL | re.MULTILINE )
return parser[ : match.start( 1 ) ] + new_rules + parser[ match.end( 1 ) : ]
def Main():
dev_parser = FileContents( DEV_PARSER_FILE )
prelude = StripRules( dev_parser )
prelude = InlineModules( DEV_PARSER_FILE, prelude )
prelude = StripTests( prelude )
prelude = StripComments( prelude )
prelude = StripCrateId( prelude )
prelude = StripExtraWhitespace( prelude )
prelude = PutPlaceholderForTopRule( prelude )
prelude = PreludeWrap( prelude )
with codecs.open( PRELUDE_FILE, 'w+', 'utf-8' ) as f:
f.write( prelude )
subprocess.check_output( [ 'cargo', 'build', '--release' ] )
inlined_parser = subprocess.check_output(
[ './target/release/nailgun', '-g', INPUT_PEG_FILE ] )
with codecs.open( INLINED_PARSER_FILE, 'w+', 'utf-8' ) as f:
f.write( inlined_parser )
with codecs.open( DEV_PARSER_FILE, 'w+', 'utf-8' ) as f:
f.write( ReplaceRules( dev_parser, ExtractRules( inlined_parser ) ) )
if __name__ == "__main__":
Main()