Transpiler for HTML-in-PowerShell, PSX (like JSX)
at master 233 lines 4.2 kB view raw
1param( 2 [Int[]] $DumpTest, 3 [Switch] $DumpAll 4) 5 6. ./Compiler.ps1 7 8$OPEN_ELEMENT_START = [Token]::new( [TokenType]::OPEN_ELEMENT_START, '<' ) 9$EQUALS = [Token]::new( [TokenType]::EQUAL, '=' ) 10$CLOSE_ELEMENT = [Token]::new( [TokenType]::CLOSE_ELEMENT, '>' ) 11$VOID_ELEMENT_CLOSE = [Token]::new( [TokenType]::VOID_ELEMENT_CLOSE, '/>' ) 12$OPEN_ELEMENT_END = [Token]::new( [TokenType]::OPEN_ELEMENT_END, '</' ) 13$EOF = [Token]::new( [TokenType]::EOF, '' ) 14 15function _tag_token($tag) { 16 return [Token]::new( [TokenType]::TAG_NAME, $tag ) 17} 18 19$Tests = @( 20 @{ 21 Name = "OPEN_ELEMENT_START" 22 Input = '<' 23 Expect = @( 24 $OPEN_ELEMENT_START, 25 $EOF 26 ) 27 }, 28 @{ 29 Name = "Opener and tag name" 30 Input = "<div" 31 Expect = @( 32 $OPEN_ELEMENT_START, 33 [Token]::new( 34 [TokenType]::TAG_NAME, 35 'div' 36 ), 37 $EOF 38 ) 39 }, 40 @{ 41 Name = "Empty element" 42 Input = "<div></div>" 43 Expect = @( 44 $OPEN_ELEMENT_START, 45 (_tag_token div), 46 $CLOSE_ELEMENT, 47 $OPEN_ELEMENT_END, 48 (_tag_token div), 49 $CLOSE_ELEMENT, 50 $EOF 51 ) 52 }, 53 @{ 54 Name = "Empty void element" 55 Input = "<img/>" 56 Expect = @( 57 $OPEN_ELEMENT_START, 58 [Token]::new( 59 [TokenType]::TAG_NAME, 60 'img' 61 ), 62 $VOID_ELEMENT_CLOSE, 63 $EOF 64 ) 65 } 66 @{ 67 Name = "Ignore extra whitespace (empty element)" 68 Input = "<`n`n div `t`n ></ div `n `t`r >" 69 Expect = @( 70 $OPEN_ELEMENT_START, 71 [Token]::new( 72 [TokenType]::TAG_NAME, 73 'div' 74 ), 75 $CLOSE_ELEMENT, 76 $OPEN_ELEMENT_END, 77 [Token]::new( 78 [TokenType]::TAG_NAME, 79 'div' 80 ), 81 $CLOSE_ELEMENT, 82 $EOF 83 ) 84 }, 85 @{ 86 Name = "Empty element with attributes" 87 Input = "<span attr1=`"bau bau`" attr2=awa></span>" 88 Expect = @( 89 $OPEN_ELEMENT_START, 90 (_tag_token span), 91 [Token]::new( 92 [TokenType]::ATTR_NAME, 93 'attr1' 94 ), 95 $EQUALS, 96 [Token]::new( 97 [TokenType]::ATTR_VALUE, 98 'bau bau' 99 ), 100 [Token]::new( 101 [TokenType]::ATTR_NAME, 102 'attr2' 103 ), 104 $EQUALS, 105 [Token]::new( 106 [TokenType]::ATTR_VALUE, 107 'awa' 108 ), 109 $CLOSE_ELEMENT, 110 $OPEN_ELEMENT_END, 111 (_tag_token span), 112 $CLOSE_ELEMENT, 113 $EOF 114 ) 115 }, 116 @{ 117 Name = "Attribute with pure scriptblock" 118 Input = '<img attrx={ $_ ; ''hello'' "there" """wao"""; return "<baubau knrs>" } />' 119 Expect = @( 120 $OPEN_ELEMENT_START, 121 (_tag_token img), 122 [Token]::new( 123 [TokenType]::ATTR_NAME, 124 'attrx' 125 ), 126 $EQUALS, 127 [Token]::new( 128 [TokenType]::ATTR_VALUE_SCRIPTBLOCK, 129 ' $_ ; ''hello'' "there" """wao"""; return "<baubau knrs>" ' 130 ), 131 $VOID_ELEMENT_CLOSE, 132 $EOF 133 ) 134 }, 135 @{ 136 Name = "Attribute with embedded scriptblock" 137 Input = '<img attrx="bau $( $_ )$(gci Function: )uab" />' 138 Expect = @( 139 $OPEN_ELEMENT_START, 140 (_tag_token img), 141 [Token]::new( 142 [TokenType]::ATTR_NAME, 143 'attrx' 144 ), 145 $EQUALS, 146 [Token]::new( 147 [TokenType]::ATTR_VALUE, 148 'bau $( $_ )$(gci Function: )uab' 149 ), 150 $VOID_ELEMENT_CLOSE, 151 $EOF 152 ) 153 }, 154 @{ 155 Name = "Splat" 156 Input = '<div attr1="one two" @Splat_Me three></div>' 157 Expect = @( 158 $OPEN_ELEMENT_START 159 (_tag_token div) 160 [Token]::new( 161 [TokenType]::ATTR_NAME, 162 'attr1' 163 ), 164 $EQUALS 165 [Token]::new( 166 [TokenType]::ATTR_VALUE, 167 'one two' 168 ), 169 [Token]::new( 170 [TokenType]::ATTR_SPLAT, 171 'Splat_Me' 172 ), 173 [Token]::new( 174 [TokenType]::ATTR_NAME, 175 'three' 176 ), 177 $CLOSE_ELEMENT, 178 $OPEN_ELEMENT_END, 179 (_tag_token div), 180 $CLOSE_ELEMENT, 181 $EOF 182 ) 183 } 184) 185 186$t=0 187$Tests | % { 188 $Lexer = [Lexer]::new($_.Input) 189 $i=0 190 191 if ($t -in $DumpTest -or $DumpAll) { 192 Write-Host "Dumping for test $t ""$($_.Name)""" 193 Write-Host "Input: $($_.Input)" 194 } 195 196 for ($i = 0; $i -lt $_.Expect.Count; $i++) { 197 $Case = $_.Expect[$i] 198 $PrePos = $Lexer.Pos 199 $PreState = $Lexer.State 200 $Next = $Lexer.NextToken() 201 202 if ($t -in $DumpTest -or $DumpAll) { 203 Write-Host (@" 204 {0} 205 [ pre-pos: {2}, pre-state: {1} ] 206 [ pst-pos: {3}, pst-state: {4} ] 207 -> {5} 208 <- {6} 209"@ -f ( 210 $i, 211 $PreState, 212 $PrePos, 213 $Lexer.Pos, 214 $Lexer.State, 215 $Case, 216 $Next 217)) 218 } 219 220 if ($Case -ne $Next) { 221 throw ("Test ""{0}"" case {1} failed: expected {2}, got {3}" -f ` 222 $_.Name, 223 $i, 224 "[ $Case ]", 225 "[ $Next ]" 226 ) 227 } 228 } 229 230 $t++ 231} 232 233Write-Host "All $($Tests.Count) test(s) passed"