@@ -22,21 +22,43 @@ use parser::{
2222pub mod call_expr;
2323mod test_harness;
2424
25+ #[ derive( Debug , PartialEq ) ]
26+ pub enum ParserMode {
27+ Test ,
28+ Autoload ,
29+ Standalone ,
30+ }
31+
32+ #[ derive( Debug ) ]
33+ pub struct ParserOpts {
34+ pub mode : ParserMode ,
35+ }
36+
2537#[ derive( Debug ) ]
2638pub struct State {
27- pub augroup : Option < Literal > ,
28- pub is_test : bool ,
39+ pub opts : ParserOpts ,
2940
3041 pub command_depth : i32 ,
3142 pub method_depth : i32 ,
3243
44+ // TODO: Consider moving augroup -> scopes
45+ // with a new scope type of augroup.
46+ //
47+ // That might be a nicer strategy (and mirrors
48+ // the same thing as we've done before).
49+ pub augroup : Option < Literal > ,
50+
3351 // TODO: We could modify the state as we are generating code.
3452 // As we generate the code and notice certain identifiers are certain
3553 // types, we can use that to do *some* optimizations
3654 pub scopes : Vec < Scope > ,
3755}
3856
3957impl State {
58+ fn is_test_mode ( & self ) -> bool {
59+ self . opts . mode == ParserMode :: Test
60+ }
61+
4062 fn is_top_level ( & self ) -> bool {
4163 self . scopes . len ( ) == 1
4264 }
@@ -76,6 +98,7 @@ impl State {
7698 . push_declaration ( expr_1, expr_2)
7799 }
78100
101+ #[ allow( unused) ]
79102 fn lookup_declaration ( & self , expr_1 : & Expression ) -> Option < Type > {
80103 match Scope :: declaration_key ( expr_1) {
81104 Some ( key) => self
@@ -602,7 +625,7 @@ impl Generate for DefCommand {
602625 let ( body, scope) =
603626 state. with_scope ( ScopeKind :: Function , |s| self . body . gen ( s) ) ;
604627
605- if state. is_test && name. starts_with ( "Test" ) {
628+ if state. opts . mode == ParserMode :: Test && name. starts_with ( "Test" ) {
606629 assert ! ( scope. deferred == 0 , "have not handled deferred in tests" ) ;
607630
608631 format ! (
@@ -1006,6 +1029,17 @@ impl Generate for Identifier {
10061029
10071030impl Generate for ScopedIdentifier {
10081031 fn gen ( & self , state : & mut State ) -> String {
1032+ if self . scope == VimScope :: VimVar {
1033+ if let Identifier :: Raw ( raw) = self . accessor . as_ref ( ) {
1034+ if raw. name == "version" {
1035+ // "lie" to vim9script code and say that we are
1036+ // version 9. This may need to be updated if
1037+ // vim updates their v:version variable
1038+ return "900" . to_string ( ) ;
1039+ }
1040+ }
1041+ }
1042+
10091043 let scope = match self . scope {
10101044 VimScope :: Global => "vim.g" ,
10111045 VimScope :: VimVar => "vim.v" ,
@@ -1419,13 +1453,13 @@ impl Generate for InfixExpression {
14191453 }
14201454}
14211455
1422- fn toplevel_id ( s : & mut State , command : & ExCommand ) -> Option < String > {
1456+ fn toplevel_ident ( s : & mut State , command : & ExCommand ) -> Option < String > {
14231457 match command {
14241458 ExCommand :: Decl ( decl) => Some ( decl. name . gen ( s) ) ,
14251459 ExCommand :: Def ( def) => {
14261460 def. name . is_valid_local ( ) . then_some ( def. name . gen ( s) )
14271461 }
1428- ExCommand :: ExportCommand ( e) => toplevel_id ( s, e. command . as_ref ( ) ) ,
1462+ ExCommand :: ExportCommand ( e) => toplevel_ident ( s, e. command . as_ref ( ) ) ,
14291463 ExCommand :: Heredoc ( here) => Some ( here. name . gen ( s) ) ,
14301464 // This might make sense, but I don't think it allows you to do this?
14311465 ExCommand :: Var ( _) => None ,
@@ -1457,26 +1491,26 @@ fn toplevel_id(s: &mut State, command: &ExCommand) -> Option<String> {
14571491 }
14581492}
14591493
1460- pub fn eval ( program : parser:: Program , is_test : bool ) -> String {
1494+ pub fn eval ( program : parser:: Program , opts : ParserOpts ) -> String {
14611495 let mut state = State {
14621496 augroup : None ,
14631497 command_depth : 0 ,
14641498 method_depth : 0 ,
14651499 scopes : vec ! [ Scope :: new( ScopeKind :: TopLevel ) ] ,
1466- is_test ,
1500+ opts ,
14671501 } ;
14681502
14691503 let mut output = String :: new ( ) ;
14701504 output += "local NVIM9 = require('vim9script')" ;
14711505 output += "local __VIM9_MODULE = {}\n " ;
14721506
1473- if is_test {
1507+ if state . is_test_mode ( ) {
14741508 output += "describe(\" filename\" , function()\n "
14751509 }
14761510
14771511 // "hoist" top-level declaractions to top of program.
14781512 for command in program. commands . iter ( ) {
1479- if let Some ( toplevel) = toplevel_id ( & mut state, command) {
1513+ if let Some ( toplevel) = toplevel_ident ( & mut state, command) {
14801514 output += & format ! ( "local {} = nil\n " , toplevel) ;
14811515 }
14821516 }
@@ -1486,7 +1520,7 @@ pub fn eval(program: parser::Program, is_test: bool) -> String {
14861520 output += "\n " ;
14871521 }
14881522
1489- if is_test {
1523+ if state . is_test_mode ( ) {
14901524 output += "end)"
14911525 }
14921526
@@ -1497,13 +1531,13 @@ pub fn eval(program: parser::Program, is_test: bool) -> String {
14971531
14981532pub fn generate (
14991533 contents : & str ,
1500- is_test : bool ,
1534+ opts : ParserOpts ,
15011535) -> Result < String , ( String , String ) > {
15021536 let lexer = Lexer :: new ( contents) ;
15031537 let parser = new_parser ( & lexer) ;
15041538 let program = parser. parse_program ( ) ;
15051539
1506- let result = eval ( program, is_test ) ;
1540+ let result = eval ( program, opts ) ;
15071541 // println!("{}", result);
15081542
15091543 let config = stylua_lib:: Config :: new ( )
@@ -1539,7 +1573,13 @@ mod test {
15391573 let mut settings = insta:: Settings :: clone_current( ) ;
15401574 settings. set_snapshot_path( "../testdata/output/" ) ;
15411575 settings. bind( || {
1542- insta:: assert_snapshot!( generate( contents, false ) . unwrap( ) ) ;
1576+ insta:: assert_snapshot!( generate(
1577+ contents,
1578+ ParserOpts {
1579+ mode: ParserMode :: Standalone ,
1580+ }
1581+ )
1582+ . unwrap( ) ) ;
15431583 } ) ;
15441584 }
15451585 } ;
@@ -1550,7 +1590,13 @@ mod test {
15501590 #[ test]
15511591 fn $name( ) {
15521592 let vim_contents = include_str!( $path) ;
1553- let lua_contents = generate( vim_contents, true ) . unwrap( ) ;
1593+ let lua_contents = generate(
1594+ vim_contents,
1595+ ParserOpts {
1596+ mode: ParserMode :: Test ,
1597+ } ,
1598+ )
1599+ . unwrap( ) ;
15541600
15551601 let filepath = concat!(
15561602 env!( "CARGO_MANIFEST_DIR" ) ,
@@ -1608,7 +1654,13 @@ mod test {
16081654 export var x = MyCoolFunc() + 1
16091655 "# ;
16101656
1611- let generated = generate ( contents, false ) . unwrap ( ) ;
1657+ let generated = generate (
1658+ contents,
1659+ ParserOpts {
1660+ mode : ParserMode :: Standalone ,
1661+ } ,
1662+ )
1663+ . unwrap ( ) ;
16121664 let eval = exec_lua ( & generated) . unwrap ( ) ;
16131665 assert_eq ! ( eval[ "x" ] , 6 . into( ) ) ;
16141666 }
@@ -1621,7 +1673,13 @@ mod test {
16211673 export var x = len("hello")
16221674 "# ;
16231675
1624- let generated = generate ( contents, false ) . unwrap ( ) ;
1676+ let generated = generate (
1677+ contents,
1678+ ParserOpts {
1679+ mode : ParserMode :: Standalone ,
1680+ } ,
1681+ )
1682+ . unwrap ( ) ;
16251683 let eval = exec_lua ( & generated) . unwrap ( ) ;
16261684 assert_eq ! ( eval[ "x" ] , 5 . into( ) ) ;
16271685 }
@@ -1643,7 +1701,13 @@ mod test {
16431701 export var x = len(nvim_get_autocmds({group: "matchparen"}))
16441702 "# ;
16451703
1646- let generated = generate ( contents, false ) . unwrap ( ) ;
1704+ let generated = generate (
1705+ contents,
1706+ ParserOpts {
1707+ mode : ParserMode :: Standalone ,
1708+ } ,
1709+ )
1710+ . unwrap ( ) ;
16471711 let eval = exec_lua ( & generated) . unwrap ( ) ;
16481712 assert_eq ! ( eval[ "x" ] , 4 . into( ) ) ;
16491713 }
0 commit comments