1- use super :: Diagnostic ;
1+ use crate :: { extract_spans , Diagnostic } ;
22use bincode;
33use proc_macro2:: { Span , TokenStream as TokenStream2 } ;
44use quote:: quote;
55use rustpython_compiler:: { bytecode:: CodeObject , compile} ;
66use std:: env;
77use std:: fs;
8- use std:: path:: { Path , PathBuf } ;
8+ use std:: path:: PathBuf ;
99use syn:: parse:: { Parse , ParseStream , Result as ParseResult } ;
10- use syn:: { self , parse2, Ident , Lit , LitByteStr , Meta , MetaList , NestedMeta , Token } ;
10+ use syn:: { self , parse2, Lit , LitByteStr , Meta , Token } ;
1111
12- struct BytecodeConst {
13- ident : Ident ,
14- meta : MetaList ,
12+ enum CompilationSourceKind {
13+ File ( PathBuf ) ,
14+ SourceCode ( String ) ,
1515}
1616
17- impl BytecodeConst {
18- fn compile ( & self , manifest_dir : & Path ) -> Result < CodeObject , Diagnostic > {
19- let meta = & self . meta ;
17+ struct CompilationSource {
18+ kind : CompilationSourceKind ,
19+ span : ( Span , Span ) ,
20+ }
21+
22+ impl CompilationSource {
23+ fn compile ( self , mode : & compile:: Mode , source_path : String ) -> Result < CodeObject , Diagnostic > {
24+ let compile = |source| {
25+ compile:: compile ( source, mode, source_path) . map_err ( |err| {
26+ Diagnostic :: spans_error ( self . span , format ! ( "Compile error: {}" , err) )
27+ } )
28+ } ;
29+
30+ match & self . kind {
31+ CompilationSourceKind :: File ( rel_path) => {
32+ let mut path = PathBuf :: from (
33+ env:: var_os ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR is not present" ) ,
34+ ) ;
35+ path. push ( rel_path) ;
36+ let source = fs:: read_to_string ( & path) . map_err ( |err| {
37+ Diagnostic :: spans_error (
38+ self . span ,
39+ format ! ( "Error reading file {:?}: {}" , path, err) ,
40+ )
41+ } ) ?;
42+ compile ( & source)
43+ }
44+ CompilationSourceKind :: SourceCode ( code) => compile ( code) ,
45+ }
46+ }
47+ }
48+
49+ struct PyCompileInput {
50+ span : Span ,
51+ metas : Vec < Meta > ,
52+ }
2053
54+ impl PyCompileInput {
55+ fn compile ( & self ) -> Result < CodeObject , Diagnostic > {
2156 let mut source_path = None ;
2257 let mut mode = None ;
23- let mut source_lit = None ;
58+ let mut source: Option < CompilationSource > = None ;
59+
60+ fn assert_source_empty ( source : & Option < CompilationSource > ) -> Result < ( ) , Diagnostic > {
61+ if let Some ( source) = source {
62+ Err ( Diagnostic :: spans_error (
63+ source. span . clone ( ) ,
64+ "Cannot have more than one source" ,
65+ ) )
66+ } else {
67+ Ok ( ( ) )
68+ }
69+ }
2470
25- for meta in & meta . nested {
71+ for meta in & self . metas {
2672 match meta {
27- NestedMeta :: Literal ( lit) => source_lit = Some ( lit) ,
28- NestedMeta :: Meta ( Meta :: NameValue ( name_value) ) => {
73+ Meta :: NameValue ( name_value) => {
2974 if name_value. ident == "mode" {
3075 mode = Some ( match & name_value. lit {
3176 Lit :: Str ( s) => match s. value ( ) . as_str ( ) {
@@ -41,94 +86,69 @@ impl BytecodeConst {
4186 Lit :: Str ( s) => s. value ( ) ,
4287 _ => bail_span ! ( name_value. lit, "source_path must be string" ) ,
4388 } )
89+ } else if name_value. ident == "source" {
90+ assert_source_empty ( & source) ?;
91+ let code = match & name_value. lit {
92+ Lit :: Str ( s) => s. value ( ) ,
93+ _ => bail_span ! ( name_value. lit, "source must be a string" ) ,
94+ } ;
95+ source = Some ( CompilationSource {
96+ kind : CompilationSourceKind :: SourceCode ( code) ,
97+ span : extract_spans ( & name_value) . unwrap ( ) ,
98+ } ) ;
99+ } else if name_value. ident == "file" {
100+ assert_source_empty ( & source) ?;
101+ let path = match & name_value. lit {
102+ Lit :: Str ( s) => PathBuf :: from ( s. value ( ) ) ,
103+ _ => bail_span ! ( name_value. lit, "source must be a string" ) ,
104+ } ;
105+ source = Some ( CompilationSource {
106+ kind : CompilationSourceKind :: File ( path) ,
107+ span : extract_spans ( & name_value) . unwrap ( ) ,
108+ } ) ;
44109 }
45110 }
46111 _ => { }
47112 }
48113 }
49114
50- let source = if meta. ident == "file" {
51- let path = match source_lit {
52- Some ( Lit :: Str ( s) ) => s. value ( ) ,
53- _ => bail_span ! ( source_lit, "Expected string literal for path to file()" ) ,
54- } ;
55- let path = manifest_dir. join ( path) ;
56- fs:: read_to_string ( & path)
57- . map_err ( |err| err_span ! ( source_lit, "Error reading file {:?}: {}" , path, err) ) ?
58- } else if meta. ident == "source" {
59- match source_lit {
60- Some ( Lit :: Str ( s) ) => s. value ( ) ,
61- _ => bail_span ! ( source_lit, "Expected string literal for source()" ) ,
62- }
63- } else {
64- bail_span ! ( meta. ident, "Expected either 'file' or 'source'" )
65- } ;
66-
67- compile:: compile (
68- & source,
69- & mode. unwrap_or ( compile:: Mode :: Exec ) ,
70- source_path. unwrap_or_else ( || "frozen" . to_string ( ) ) ,
71- )
72- . map_err ( |err| err_span ! ( source_lit, "Compile error: {}" , err) )
73- }
74- }
75-
76- impl Parse for BytecodeConst {
77- /// Parse the form `static ref IDENT = metalist(...);`
78- fn parse ( input : ParseStream ) -> ParseResult < Self > {
79- input. parse :: < Token ! [ static ] > ( ) ?;
80- input. parse :: < Token ! [ ref] > ( ) ?;
81- let ident = input. parse ( ) ?;
82- input. parse :: < Token ! [ =] > ( ) ?;
83- let meta = input. parse ( ) ?;
84- input. parse :: < Token ! [ ; ] > ( ) ?;
85- Ok ( BytecodeConst { ident, meta } )
115+ source
116+ . ok_or_else ( || {
117+ Diagnostic :: span_error (
118+ self . span . clone ( ) ,
119+ "Must have either file or source in py_compile_bytecode!()" ,
120+ )
121+ } ) ?
122+ . compile (
123+ & mode. unwrap_or ( compile:: Mode :: Exec ) ,
124+ source_path. unwrap_or_else ( || "frozen" . to_string ( ) ) ,
125+ )
86126 }
87127}
88128
89- struct PyCompileInput ( Vec < BytecodeConst > ) ;
90-
91129impl Parse for PyCompileInput {
92130 fn parse ( input : ParseStream ) -> ParseResult < Self > {
93- std:: iter:: from_fn ( || {
94- if input. is_empty ( ) {
95- None
96- } else {
97- Some ( input. parse ( ) )
98- }
99- } )
100- . collect :: < ParseResult < _ > > ( )
101- . map ( PyCompileInput )
131+ let span = input. cursor ( ) . span ( ) ;
132+ let metas = input
133+ . parse_terminated :: < Meta , Token ! [ , ] > ( Meta :: parse) ?
134+ . into_iter ( )
135+ . collect ( ) ;
136+ Ok ( PyCompileInput { span, metas } )
102137 }
103138}
104139
105140pub fn impl_py_compile_bytecode ( input : TokenStream2 ) -> Result < TokenStream2 , Diagnostic > {
106- let PyCompileInput ( consts) = parse2 ( input) ?;
107-
108- let manifest_dir = PathBuf :: from (
109- env:: var_os ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR is not present" ) ,
110- ) ;
141+ let input: PyCompileInput = parse2 ( input) ?;
111142
112- let consts = consts
113- . into_iter ( )
114- . map ( |bytecode_const| -> Result < _ , Diagnostic > {
115- let code_obj = bytecode_const. compile ( & manifest_dir) ?;
116- let ident = bytecode_const. ident ;
117- let bytes = bincode:: serialize ( & code_obj) . expect ( "Failed to serialize" ) ;
118- let bytes = LitByteStr :: new ( & bytes, Span :: call_site ( ) ) ;
119- Ok ( quote ! {
120- static ref #ident: :: rustpython_vm:: bytecode:: CodeObject = {
121- use bincode;
122- bincode:: deserialize( #bytes) . expect( "Deserializing CodeObject failed" )
123- } ;
124- } )
125- } )
126- . collect :: < Result < Vec < _ > , _ > > ( ) ?;
143+ let code_obj = input. compile ( ) ?;
144+ let bytes = bincode:: serialize ( & code_obj) . expect ( "Failed to serialize" ) ;
145+ let bytes = LitByteStr :: new ( & bytes, Span :: call_site ( ) ) ;
127146
128147 let output = quote ! {
129- lazy_static! {
130- #( #consts) *
131- }
148+ ( {
149+ use bincode;
150+ bincode:: deserialize( #bytes) . expect( "Deserializing CodeObject failed" )
151+ } )
132152 } ;
133153
134154 Ok ( output)
0 commit comments