├── src ├── go.mod ├── go.sum ├── main.go ├── ast.y ├── lexer.l ├── codegen.go ├── parser.go ├── ast.go └── lexer.go ├── LICENSE └── README.md /src/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thooton/jc 2 | 3 | go 1.19 4 | 5 | require github.com/evanw/esbuild v0.16.12 6 | 7 | require ( 8 | golang.org/x/sys v0.3.0 // indirect 9 | golang.org/x/tools v0.4.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /src/go.sum: -------------------------------------------------------------------------------- 1 | github.com/evanw/esbuild v0.16.12 h1:B9hb5GRgRkGYmq582yGGPwSFks769bJOPksdKaEERXw= 2 | github.com/evanw/esbuild v0.16.12/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk= 3 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= 4 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 5 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 6 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 7 | golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= 8 | golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 thooton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | ) 11 | 12 | /* 13 | func debugNode(n *AstNode, input []byte) { 14 | put := func(s string) { 15 | fmt.Print(" " + s + " ") 16 | } 17 | cont := func() { 18 | for i := range n.nodes { 19 | debugNode(&n.nodes[i], input) 20 | } 21 | } 22 | lit := func() { 23 | fmt.Print(" " + string(input[n.token.begin:n.token.end]) + " ") 24 | } 25 | lst := func(name string) { 26 | put(name + ":") 27 | cont() 28 | put(":" + name + " end") 29 | } 30 | tok := func(name string) { 31 | put("[" + name) 32 | lit() 33 | put("]") 34 | } 35 | switch n.token.kind { 36 | case AstBlockUnfin: 37 | lst("unfinished block") 38 | break 39 | case AstBlock: 40 | lst("block") 41 | break 42 | case AstQuoteUnfin: 43 | lst("unfinished quote") 44 | break 45 | case AstQuote: 46 | lst("quote") 47 | break 48 | case AstTicklitUnfin: 49 | lst("unfinished ticklit") 50 | break 51 | case AstTicklit: 52 | lst("ticklit") 53 | break 54 | case AstInterpolUnfin: 55 | lst("unfinished interpol") 56 | break 57 | case AstInterpol: 58 | lst("interpol") 59 | break 60 | case AstToplevel: 61 | lst("toplevel") 62 | break 63 | case AstMacro: 64 | lst("macro") 65 | break 66 | case AstLfnArgs: 67 | lst("lfn args") 68 | break 69 | case AstLfnRetType: 70 | lst("lfn ret type") 71 | break 72 | case AstLfnBody: 73 | lst("lfn body") 74 | break 75 | case TMacroStart: 76 | tok("macro start") 77 | break 78 | case TIdentifier: 79 | tok("identifier") 80 | break 81 | case TConst: 82 | tok("const") 83 | break 84 | case TStructUnionEnumClass: 85 | tok("seu") 86 | break 87 | case TJsOnly: 88 | tok("jsonly") 89 | break 90 | case TMinusGt: 91 | tok("minusgt") 92 | break 93 | case TEq: 94 | tok("eq") 95 | break 96 | case TLSquare: 97 | tok("lsquare") 98 | break 99 | case TRSquare: 100 | tok("rsquare") 101 | break 102 | case TOther: 103 | tok("other") 104 | break 105 | case TLBrace: 106 | tok("lbrace") 107 | break 108 | case TRBrace: 109 | tok("rbrace") 110 | break 111 | case TQuote: 112 | tok("quote") 113 | break 114 | case TDollarLBrace: 115 | tok("dollarlbrace") 116 | break 117 | case TTick: 118 | tok("tick") 119 | break 120 | case TLCircle: 121 | tok("lcircle") 122 | break 123 | case TRCircle: 124 | tok("rcircle") 125 | break 126 | case TSemi: 127 | tok("semi") 128 | break 129 | } 130 | }*/ 131 | 132 | func main() { 133 | if len(os.Args) != 7 || 134 | os.Args[1] != "-i" || 135 | os.Args[3] != "-o" || 136 | os.Args[5] != "-n" { 137 | fmt.Println("usage: jc -i -o -n ") 138 | os.Exit(1) 139 | } 140 | astErrorVerbose = true 141 | infile := os.Args[2] 142 | outfile := os.Args[4] 143 | interpreter := os.Args[6] 144 | input, err := os.ReadFile(infile) 145 | if err != nil { 146 | fmt.Printf("can't read file %s: %v\n", infile, err) 147 | os.Exit(1) 148 | } 149 | root, estr := parseIntoAst(input) 150 | if estr != "" { 151 | fmt.Printf("parsing error: %s\n", estr) 152 | os.Exit(1) 153 | } 154 | //debugNode(&root, input) 155 | result := codegenPerform(&root, input, outfile) 156 | dotidx := strings.LastIndexByte(infile, '.') 157 | var tmpfile string 158 | if dotidx != -1 { 159 | tmpfile = infile[0:dotidx] + "_" + infile[dotidx+1:] + "_gen.js" 160 | } else { 161 | tmpfile = infile + "_gen.js" 162 | } 163 | err = os.WriteFile(tmpfile, result, 0o644) 164 | if err != nil { 165 | fmt.Printf("can't write to %s: %v\n", tmpfile, err) 166 | os.Exit(1) 167 | } 168 | _ = os.Remove(outfile) 169 | cmd := exec.Command(interpreter, tmpfile) 170 | cmd.Stdin = nil 171 | cmd.Stdout = os.Stdout 172 | cmd.Stderr = os.Stderr 173 | err = cmd.Start() 174 | if err != nil { 175 | fmt.Printf("can't start interpreter %s: %v\n", interpreter, err) 176 | os.Exit(1) 177 | } 178 | _ = cmd.Wait() 179 | _, err = os.Lstat(outfile) 180 | if err != nil { 181 | os.Exit(1) 182 | } else { 183 | _ = os.Remove(tmpfile) 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/ast.y: -------------------------------------------------------------------------------- 1 | %{ 2 | package main 3 | const ( 4 | AstBlockUnfin = 10 5 | AstBlock = 11 6 | AstQCircleUnfin = 12 7 | AstQCircle = 13 8 | AstQuoteUnfin = 14 9 | AstQuote = 15 10 | AstTicklitUnfin = 16 11 | AstTicklit = 17 12 | AstInterpolUnfin = 18 13 | AstInterpol = 19 14 | AstToplevel = 20 15 | AstMacro = 21 16 | AstLfnArgs = 22 17 | AstLfnRetType = 23 18 | AstLfnBody = 24 19 | AstEnd = 25 20 | ) 21 | /* this is a "union" between 22 | token and nodes. all of 23 | the actual token constants are enormous, 24 | so if token.kind < AstEnd, we know it is an 25 | []AstNode instead. */ 26 | type AstNode struct { 27 | token LexToken 28 | nodes []AstNode 29 | } 30 | %} 31 | 32 | %token TIdentifier 33 | %token TConst TStructUnionEnumClass TFunctionForWhileJsclass TJsOnly 34 | %token TMinusGt 35 | %token TEq 36 | %token TLSquare TRSquare 37 | %token TOther 38 | 39 | %token TLBrace TRBrace 40 | %token TQuote TDollarLBrace TTick 41 | %token TLCircle TRCircle 42 | %token TSemi 43 | 44 | %token TMacroStart TMacroEnd 45 | 46 | %union { 47 | node AstNode 48 | } 49 | 50 | %type TIdentifier TConst TStructUnionEnumClass TFunctionForWhileJsclass TJsOnly TMinusGt 51 | %type TEq TLSquare TRSquare TOther 52 | %type TLBrace TRBrace TQuote TDollarLBrace TTick TLCircle TRCircle 53 | %type TSemi 54 | %type TMacroStart TMacroEnd 55 | 56 | %type harmless block_unfinished block quote_harmless quote_circle_unfinished quote_circle quote_unfinished quote 57 | %type ticklit_unfinished ticklit interpol_unfinished interpol toplevel result 58 | %type tl_interpol_unfinished tl_interpol 59 | 60 | %type block_unfinished_harmless all_interpol_harmless toplevel_harmless 61 | %type macro_harmless macro_unfinished macro 62 | 63 | %start result 64 | 65 | /* note: compile with 66 | go run golang.org/x/tools/cmd/goyacc -o ast.go -p ast ast.y */ 67 | 68 | /* note: I manually change the parser parameter 69 | astLexer in the generated code to 70 | the custom ParserLexer defined in parser.go 71 | */ 72 | 73 | %% 74 | 75 | harmless 76 | : TIdentifier 77 | | TConst 78 | | TStructUnionEnumClass 79 | | TFunctionForWhileJsclass 80 | | TJsOnly 81 | | TMinusGt 82 | | TEq 83 | | TLSquare 84 | | TRSquare 85 | | TOther 86 | | TSemi 87 | ; 88 | 89 | block_unfinished_harmless 90 | : harmless 91 | | TLCircle 92 | | TRCircle 93 | | quote 94 | | ticklit 95 | | interpol 96 | | block 97 | ; 98 | block_unfinished 99 | : TLBrace { astlex.beginBlock(); $$ = AstNode{LexToken{0, 0, AstBlockUnfin}, []AstNode{$1}} } 100 | | block_unfinished block_unfinished_harmless { $1.nodes = append($1.nodes, $2); $$ = $1 } 101 | ; 102 | block 103 | : block_unfinished TRBrace { astlex.endBlock(); $$ = AstNode{LexToken{0, 0, AstBlock}, append($1.nodes, $2)} } 104 | ; 105 | 106 | quote_harmless 107 | : harmless 108 | | TLBrace 109 | | TRBrace 110 | | TQuote 111 | | TTick 112 | | interpol 113 | ; 114 | quote_circle_unfinished 115 | : TLCircle { $$ = AstNode{LexToken{0, 0, AstQCircleUnfin}, []AstNode{$1}}} 116 | | quote_circle_unfinished quote_harmless { $1.nodes = append($1.nodes, $2); $$ = $1 } 117 | | quote_circle_unfinished quote_circle { $1.nodes = append($1.nodes, $2.nodes...); $$ = $1 } 118 | ; 119 | quote_circle 120 | : quote_circle_unfinished TRCircle 121 | 122 | quote_unfinished 123 | : TQuote TLCircle 124 | { $$ = AstNode{LexToken{0, 0, AstQuoteUnfin}, []AstNode{$1, $2}} } 125 | | quote_unfinished quote_harmless 126 | { $1.nodes = append($1.nodes, $2); $$ = $1 } 127 | | quote_unfinished quote_circle 128 | { $1.nodes = append($1.nodes, $2.nodes...); $$ = $1 } 129 | ; 130 | quote 131 | : quote_unfinished TRCircle { $$ = AstNode{LexToken{0, 0, AstQuote}, append($1.nodes, $2)} } 132 | ; 133 | 134 | all_interpol_harmless 135 | : harmless 136 | | TLCircle 137 | | TRCircle 138 | | quote 139 | | ticklit 140 | | interpol 141 | | block 142 | ; 143 | 144 | tl_interpol_unfinished 145 | : TDollarLBrace { astlex.beginBlock(); astlex.outTicks(); $$ = AstNode{LexToken{0, 0, AstInterpolUnfin}, []AstNode{$1}} } 146 | | tl_interpol_unfinished all_interpol_harmless { $1.nodes = append($1.nodes, $2); $$ = $1 } 147 | ; 148 | tl_interpol 149 | : tl_interpol_unfinished TRBrace { astlex.endBlock(); astlex.inTicks(); $$ = AstNode{LexToken{0, 0, AstInterpol}, append($1.nodes, $2)} } 150 | ; 151 | ticklit_unfinished 152 | : TTick { astlex.inTicks(); $$ = AstNode{LexToken{0, 0, AstTicklitUnfin}, []AstNode{$1}} } 153 | | ticklit_unfinished TOther { $1.nodes = append($1.nodes, $2); $$ = $1 } 154 | | ticklit_unfinished tl_interpol { $1.nodes = append($1.nodes, $2); $$ = $1 } 155 | ; 156 | ticklit 157 | : ticklit_unfinished TTick 158 | { astlex.outTicks(); $$ = AstNode{LexToken{0, 0, AstTicklit}, append($1.nodes, $2)} } 159 | ; 160 | 161 | interpol_unfinished 162 | : TDollarLBrace { astlex.beginBlock(); $$ = AstNode{LexToken{0, 0, AstInterpolUnfin}, []AstNode{$1}} } 163 | | interpol_unfinished all_interpol_harmless { $1.nodes = append($1.nodes, $2); $$ = $1 } 164 | ; 165 | interpol 166 | : interpol_unfinished TRBrace { astlex.endBlock(); $$ = AstNode{LexToken{0, 0, AstInterpol}, append($1.nodes, $2)} } 167 | ; 168 | 169 | macro_harmless 170 | : harmless 171 | | TLBrace 172 | | TRBrace 173 | | TLCircle 174 | | TRCircle 175 | | TQuote 176 | | TTick 177 | | interpol 178 | ; 179 | macro_unfinished 180 | : TMacroStart { $$ = AstNode{LexToken{0, 0, AstMacro}, []AstNode{$1}} } 181 | | macro_unfinished macro_harmless { $1.nodes = append($1.nodes, $2); $$ = $1 } 182 | ; 183 | macro 184 | : macro_unfinished TMacroEnd 185 | 186 | toplevel_harmless 187 | : harmless 188 | | TLCircle 189 | | TRCircle 190 | | quote 191 | | ticklit 192 | | interpol 193 | | block 194 | | macro 195 | ; 196 | toplevel 197 | : toplevel_harmless { $$ = AstNode{LexToken{0, 0, AstToplevel}, []AstNode{$1}} } 198 | | toplevel toplevel_harmless { $1.nodes = append($1.nodes, $2); $$ = $1 } 199 | ; 200 | 201 | result 202 | : toplevel { astlex.receiveResult($1); return 0 } 203 | ; -------------------------------------------------------------------------------- /src/lexer.l: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "bytes" 4 | 5 | const ( 6 | TEof = 0 7 | TInvalid = 1 8 | TPLBefore = 2 9 | TPLNewline = 3 10 | TPLValue = 4 11 | TPLInc = 5 12 | TPLDec = 6 13 | TPLPound = 7 14 | TPLSlash = 8 15 | TPLAfter = 9 16 | ) 17 | 18 | type LexToken struct { 19 | begin, end, kind uint32 20 | } 21 | 22 | func lexRawQuoteLit(input []byte, cursor uint) uint { 23 | inp := input[cursor:] 24 | dseq_end := bytes.IndexByte(inp, '(') 25 | if dseq_end == -1 { 26 | return uint(len(input)) 27 | } 28 | eseq := make([]byte, 0, 2+dseq_end) 29 | eseq = append(eseq, ')') 30 | eseq = append(eseq, inp[:dseq_end]...) 31 | eseq = append(eseq, '"') 32 | eseq_loc := bytes.Index(inp[dseq_end+1:], eseq) 33 | if eseq_loc == -1 { 34 | return uint(len(input)) 35 | } 36 | return cursor+uint(dseq_end+1+eseq_loc+len(eseq)) 37 | } 38 | 39 | func lexNextToken(input []byte, cursor uint) LexToken { 40 | var began uint = cursor 41 | var marker uint = 0 42 | peek_next := func(str []byte, i uint) byte { 43 | if i < uint(len(str)) { 44 | return str[i] 45 | } else { 46 | return 0 47 | } 48 | } 49 | lex_start: 50 | /*!re2c 51 | re2c:yyfill:enable = 0; 52 | re2c:define:YYCTYPE = byte; 53 | re2c:define:YYPEEK = "peek_next(input, cursor)"; 54 | re2c:define:YYSKIP = "cursor++"; 55 | re2c:define:YYBACKUP = "marker = cursor"; 56 | re2c:define:YYRESTORE = "cursor = marker"; 57 | re2c:tags = 0; 58 | 59 | * { 60 | return LexToken{0, 0, TInvalid} 61 | } 62 | [\x00] { 63 | return LexToken{0, 0, TEof} 64 | } 65 | mcm = "/" "*"; 66 | mcm { 67 | for ;; { 68 | yych = peek_next(input, cursor) 69 | cursor++ 70 | if yych == 0 { 71 | return LexToken{0, 0, TEof} 72 | } else if yych == '*' { 73 | yych = peek_next(input, cursor) 74 | if yych == 0 { 75 | return LexToken{0, 0, TEof} 76 | } else if yych == '/' { 77 | cursor++; 78 | began = cursor 79 | goto lex_start 80 | } 81 | } 82 | } 83 | } 84 | scm = "//" [^\n\x00]*; 85 | wsp = ([\x01-\x09\x0B-\x20\x85] | scm | "\\\r\n" | "\\\n")+; 86 | wsp { 87 | began = cursor 88 | goto lex_start 89 | } 90 | 91 | "const" { 92 | return LexToken{uint32(began), uint32(cursor), TConst} 93 | } 94 | ("struct" | "union" | "enum" | "class") { 95 | return LexToken{uint32(began), uint32(cursor), TStructUnionEnumClass} 96 | } 97 | "quote" { 98 | return LexToken{uint32(began), uint32(cursor), TQuote} 99 | } 100 | "function" | "for" | "while" { 101 | return LexToken{uint32(began), uint32(cursor), TFunctionForWhileJsclass} 102 | } 103 | "jsclass" { 104 | input[began] = ' ' 105 | input[began+1] = ' ' 106 | return LexToken{uint32(began+2), uint32(cursor), TFunctionForWhileJsclass} 107 | } 108 | ("async" | "let" | "var" | "undefined") { 109 | return LexToken{uint32(began), uint32(cursor), TJsOnly} 110 | } 111 | "->" { 112 | return LexToken{uint32(began), uint32(cursor), TMinusGt} 113 | } 114 | "=" { 115 | return LexToken{uint32(began), uint32(cursor), TEq} 116 | } 117 | "[" { 118 | return LexToken{uint32(began), uint32(cursor), TLSquare} 119 | } 120 | "]" { 121 | return LexToken{uint32(began), uint32(cursor), TRSquare} 122 | } 123 | [!>.<+\-*%&|^?:,~] { 124 | return LexToken{uint32(began), uint32(cursor), TOther} 125 | } 126 | "{" { 127 | return LexToken{uint32(began), uint32(cursor), TLBrace} 128 | } 129 | "}" { 130 | return LexToken{uint32(began), uint32(cursor), TRBrace} 131 | } 132 | "quote" { 133 | return LexToken{uint32(began), uint32(cursor), TQuote} 134 | } 135 | "${" { 136 | return LexToken{uint32(began), uint32(cursor), TDollarLBrace} 137 | } 138 | "`" { 139 | return LexToken{uint32(began), uint32(cursor), TTick} 140 | } 141 | "(" { 142 | return LexToken{uint32(began), uint32(cursor), TLCircle} 143 | } 144 | ")" { 145 | return LexToken{uint32(began), uint32(cursor), TRCircle} 146 | } 147 | ";" { 148 | return LexToken{uint32(began), uint32(cursor), TSemi} 149 | } 150 | "\n" { 151 | return LexToken{uint32(began), uint32(cursor), TPLNewline} 152 | } 153 | 154 | id_start = [A-Za-z_\x80-\x84\x86-\xFF]; 155 | id_continue = [A-Za-z0-9_\x80-\x84\x86-\xFF]; 156 | identifier = id_start id_continue*; 157 | identifier { 158 | return LexToken{uint32(began), uint32(cursor), TIdentifier} 159 | } 160 | 161 | escapedchr = "\\" [^\x00]; 162 | strpfx = "L"|("u" "8"?)|"U"; 163 | dquotelit = strpfx? '"' (escapedchr | [^"\n\\\x00])* '"'; 164 | squotelit = strpfx? "'" (escapedchr | [^'\n\\\x00])* "'"; 165 | rquotelit = strpfx? "R" '"'; 166 | 167 | digit_sequence = [0-9']+; 168 | hex_digit_sequence = [0-9A-Fa-f']+; 169 | bin_digit_sequence = [01']+; 170 | 171 | declit = ("0" [oO])? digit_sequence; 172 | hexlit = ("0" [xX]) hex_digit_sequence; 173 | binlit = ("0" [bB]) bin_digit_sequence; 174 | intsfx = "n"|([lLuUzZ]+); 175 | intlit = (declit|hexlit|binlit) intsfx?; 176 | 177 | decimal_exponent = [eE] [+-]? digit_sequence; 178 | hex_exponent = [pP] [+-]? hex_digit_sequence; 179 | floatsfx = [flFL]|"f16"|"f32"|"f64"|"f128"|"bf16"|"F16"|"F32"|"F64"|"F128"|"BF16"; 180 | floatlit = ( 181 | (digit_sequence decimal_exponent) 182 | | ( 183 | ( 184 | (digit_sequence '.') 185 | | (digit_sequence? '.' digit_sequence) 186 | ) 187 | decimal_exponent? 188 | ) 189 | | ("0" [xX] 190 | ( 191 | (hex_digit_sequence '.'?) 192 | | (hex_digit_sequence? '.' hex_digit_sequence) 193 | ) 194 | hex_exponent 195 | ) 196 | ) floatsfx?; 197 | 198 | value = dquotelit | squotelit 199 | | intlit | floatlit; 200 | value { 201 | return LexToken{uint32(began), uint32(cursor), TPLValue} 202 | } 203 | rquotelit { 204 | cursor = lexRawQuoteLit(input, cursor) 205 | return LexToken{uint32(began), uint32(cursor), TPLValue} 206 | } 207 | 208 | "++" { 209 | return LexToken{uint32(began), uint32(cursor), TPLInc} 210 | } 211 | "--" { 212 | return LexToken{uint32(began), uint32(cursor), TPLDec} 213 | } 214 | "#" { 215 | return LexToken{uint32(began), uint32(cursor), TPLPound} 216 | } 217 | "/" { 218 | return LexToken{uint32(began), uint32(cursor), TPLSlash} 219 | } 220 | 221 | coperator = ">>" | "<<" | "&&" | "||" 222 | | "==" | "!=" | ">=" | "<=" | "+=" | "-=" | "*=" 223 | | "/=" | "%=" | "&=" | "|=" | "^=" 224 | | ">>=" | "<<=" | "::" | "..." | "<=>"; 225 | 226 | jsoperator = "===" | "!==" | ">==" | "<==" 227 | | ">>>" | ">>>=" | "??" | "**" | "**=" | "??="; 228 | 229 | other = coperator | jsoperator; 230 | other { 231 | return LexToken{uint32(began), uint32(cursor), TOther} 232 | } 233 | */ 234 | } 235 | 236 | func lexRegexpLit(input []byte, cursor uint) LexToken { 237 | var began uint = cursor 238 | var marker uint = 0 239 | peek_next := func(str []byte, i uint) byte { 240 | if i < uint(len(str)) { 241 | return str[i] 242 | } else { 243 | return 0 244 | } 245 | } 246 | /*!re2c 247 | * { 248 | return LexToken{0, 0, TInvalid} 249 | } 250 | [\x00] { 251 | return LexToken{0, 0, TEof} 252 | } 253 | regchr = [^/\[\n\\\x00] | escapedchr; 254 | regseq = "[" ([^/\]\n\\\x00] | escapedchr)* "]"; 255 | regexp = "/" (regchr | regseq)+ "/" [dgimsuvy]*; 256 | regexp { 257 | return LexToken{uint32(began), uint32(cursor), TOther} 258 | } 259 | */ 260 | } 261 | 262 | func lexTickLit(input []byte, cursor uint) LexToken { 263 | var began uint = cursor 264 | var ch byte 265 | for ;; { 266 | if cursor < uint(len(input)) { 267 | ch = input[cursor] 268 | cursor++ 269 | } else { 270 | return LexToken{0, 0, TInvalid} 271 | } 272 | if ch == '\\' { 273 | if cursor < uint(len(input)) { 274 | cursor++ 275 | } else { 276 | return LexToken{0, 0, TInvalid} 277 | } 278 | } else if ch == '`' { 279 | if (began+1) == cursor { 280 | return LexToken{uint32(began), uint32(cursor), TTick} 281 | } else { 282 | return LexToken{uint32(began), uint32(cursor-1), TOther} 283 | } 284 | } else if (ch == '$') { 285 | if cursor < uint(len(input)) { 286 | ch = input[cursor] 287 | cursor++ 288 | } else { 289 | return LexToken{0, 0, TInvalid} 290 | } 291 | if (ch == '{') { 292 | if (began+2) == cursor { 293 | return LexToken{uint32(began), uint32(cursor), TDollarLBrace} 294 | } else { 295 | return LexToken{uint32(began), uint32(cursor-2), TOther} 296 | } 297 | } 298 | } 299 | } 300 | } -------------------------------------------------------------------------------- /src/codegen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const js_prelude string = ` 4 | /* beginning of JC prelude */ 5 | 6 | if (typeof JC_INTERNAL_OUTFILE !== "string") { 7 | throw new Error(); 8 | } 9 | 10 | class JcInternalQuote { 11 | constructor(sections) { 12 | this.sections = sections; 13 | } 14 | add(other) { 15 | if (!(other instanceof JcInternalQuote)) { 16 | console.error("got", other); 17 | throw new Error( 18 | "bad argument 1 to quote::add: "+ 19 | "one can only add a quote to a quote" 20 | ); 21 | } 22 | var mysec = this.sections; 23 | var othersec = other.sections; 24 | for (var item of othersec) { 25 | mysec.push(item); 26 | } 27 | } 28 | plus(other) { 29 | if (!(other instanceof JcInternalQuote)) { 30 | console.error("got", other); 31 | throw new Error( 32 | "bad argument 1 to quote::plus: "+ 33 | "one can only concatenate two quotes" 34 | ); 35 | } 36 | var mysec = this.sections; 37 | var othersec = other.sections; 38 | var newsec = []; 39 | for (var item of mysec) { 40 | newsec.push(item); 41 | } 42 | for (var item of othersec) { 43 | newsec.push(item); 44 | } 45 | return new JcInternalQuote(newsec); 46 | } 47 | } 48 | 49 | const JC_INTERNAL_TOPLEVEL = []; 50 | var JC_INTERNAL_DENO = false; 51 | try { 52 | var item = Deno.writeTextFileSync; 53 | if (item) { 54 | JC_INTERNAL_DENO = true; 55 | } 56 | } catch (_e) {} 57 | 58 | function strtoq(inp) { 59 | if (typeof inp !== "string") { 60 | console.error("expected string, got", inp); 61 | throw new Error("bad argument 1 to strtoq"); 62 | } 63 | return new JcInternalQuote([inp]); 64 | } 65 | 66 | var JC_INTERNAL_NEXTSYM = 1; 67 | function gensym() { 68 | return new JcInternalQuote(["jc_sym"+(JC_INTERNAL_NEXTSYM++)]); 69 | } 70 | 71 | class JcInternalFunction { 72 | constructor(args, ret_type, body) { 73 | this.args = args; 74 | this.ret_type = ret_type; 75 | this.body = body; 76 | this.num = 0; 77 | } 78 | } 79 | 80 | const JC_INTERNAL_ENCODER = new TextEncoder(); 81 | function jcInternalToCLiteral(str) { 82 | const src = JC_INTERNAL_ENCODER.encode(str); 83 | var res = "\""; 84 | for (const ch of src) { 85 | switch (ch) { 86 | case 0x22: 87 | res += "\\\""; 88 | break; 89 | case 0x5c: 90 | res += "\\"; 91 | break; 92 | case 0x07: 93 | res += "\\a"; 94 | break; 95 | case 0x08: 96 | res += "\\b"; 97 | break; 98 | case 0x0c: 99 | res += "\\f"; 100 | break; 101 | case 0x0a: 102 | res += "\\n"; 103 | break; 104 | case 0x0d: 105 | res += "\\r"; 106 | break; 107 | case 0x09: 108 | res += "\\t"; 109 | break; 110 | case 0x0b: 111 | res += "\\v"; 112 | break; 113 | default: 114 | if (ch >= 32 && ch < 127) { 115 | res += String.fromCharCode(ch); 116 | } else { 117 | res += "\\"; 118 | res += ch.toString(8).padStart(3, "0"); 119 | } 120 | } 121 | } 122 | res += "\""; 123 | return res; 124 | } 125 | 126 | var JC_INTERNAL_OUTPUT = ""; 127 | var JC_INTERNAL_NEXT_FN = 1; 128 | async function jcInternalWrite(obj) { 129 | if (typeof obj === "string") { 130 | return jcInternalToCLiteral(obj); 131 | } else if (typeof obj === "object") { 132 | while (obj instanceof Promise) { 133 | obj = await obj; 134 | } 135 | if (Array.isArray(obj)) { 136 | if (obj.length === 0) { 137 | return "{}"; 138 | } 139 | var str = "{"; 140 | str += await jcInternalWrite(obj[0]); 141 | for (var i = 1; i < obj.length; i++) { 142 | str += ", "; 143 | str += await jcInternalWrite(obj[i]); 144 | } 145 | str += "}"; 146 | return str; 147 | } else if (obj instanceof JcInternalQuote) { 148 | var str = ""; 149 | for (const item of obj.sections) { 150 | if (typeof item === "string") { 151 | str += item; 152 | } else { 153 | str += await jcInternalWrite(item.obj); 154 | } 155 | } 156 | return str; 157 | } else if (obj instanceof JcInternalFunction) { 158 | if (obj.num === 0) { 159 | var str = "\n\n"; 160 | str += await jcInternalWrite(obj.ret_type); 161 | str += " jc_lfn"; 162 | str += (obj.num = (JC_INTERNAL_NEXT_FN++)); 163 | str += await jcInternalWrite(obj.args); 164 | str += " "; 165 | str += await jcInternalWrite(obj.body); 166 | str += "\n\n"; 167 | JC_INTERNAL_OUTPUT += str; 168 | } 169 | return "jc_lfn"+obj.num; 170 | } else { 171 | var str = "{"; 172 | var first = true; 173 | for (var key in obj) { 174 | var value = obj[key]; 175 | if (typeof value === "function") { 176 | continue; 177 | } 178 | if (first) { 179 | first = false; 180 | } else { 181 | str += ", "; 182 | } 183 | str += "."; 184 | str += key; 185 | str += "="; 186 | str += await jcInternalWrite(value); 187 | } 188 | str += "}"; 189 | return str; 190 | } 191 | } else if (typeof obj === "bigint") { 192 | return obj.toString(10); 193 | } else if (typeof obj === "number") { 194 | return obj.toString(10); 195 | } else { 196 | console.error("got", obj); 197 | throw new Error("don't know how to convert given object into C"); 198 | } 199 | } 200 | 201 | async function jcInternalFinish() { 202 | for (const item of JC_INTERNAL_TOPLEVEL) { 203 | var str = await jcInternalWrite(item); 204 | JC_INTERNAL_OUTPUT += str; 205 | } 206 | JC_INTERNAL_OUTPUT += "\n"; 207 | if (JC_INTERNAL_DENO) { 208 | Deno.writeTextFileSync( 209 | JC_INTERNAL_OUTFILE, 210 | JC_INTERNAL_OUTPUT 211 | ); 212 | } else { 213 | require("fs").writeFileSync( 214 | JC_INTERNAL_OUTFILE, 215 | JC_INTERNAL_OUTPUT 216 | ); 217 | } 218 | } 219 | 220 | /* end of JC prelude */ 221 | ` 222 | 223 | type Codegen struct { 224 | sb []byte 225 | input []byte 226 | } 227 | 228 | func pushStringLiteral(src []byte, sb []byte) []byte { 229 | sb = append(sb, '"') 230 | for i := range src { 231 | ch := src[i] 232 | switch ch { 233 | case 0x22: 234 | sb = append(sb, '\\', '"') 235 | break 236 | case 0x5c: 237 | sb = append(sb, '\\', '\\') 238 | break 239 | case 0x07: 240 | sb = append(sb, '\\', 'a') 241 | break 242 | case 0x08: 243 | sb = append(sb, '\\', 'b') 244 | break 245 | case 0x0c: 246 | sb = append(sb, '\\', 'f') 247 | break 248 | case 0x0a: 249 | sb = append(sb, '\\', 'n') 250 | break 251 | case 0x0d: 252 | sb = append(sb, '\\', 'r') 253 | break 254 | case 0x09: 255 | sb = append(sb, '\\', 't') 256 | break 257 | case 0x0b: 258 | sb = append(sb, '\\', 'v') 259 | break 260 | default: 261 | if ch >= 32 && ch < 127 { 262 | sb = append(sb, ch) 263 | } else { 264 | ch3 := ch & 8 265 | ch >>= 3 266 | ch2 := ch & 8 267 | ch >>= 3 268 | ch1 := ch & 8 269 | sb = append(sb, '\\', ch1, ch2, ch3) 270 | } 271 | } 272 | } 273 | sb = append(sb, '"') 274 | return sb 275 | } 276 | 277 | func (cg *Codegen) generateForC(nodes []AstNode) { 278 | iend_prev := startOf(&nodes[0]) 279 | for i := range nodes { 280 | kind := nodes[i].token.kind 281 | if kind > AstEnd { 282 | continue 283 | } 284 | istart := startOf(&nodes[i]) 285 | if iend_prev != istart { 286 | cg.sb = pushStringLiteral( 287 | cg.input[iend_prev:istart], 288 | cg.sb, 289 | ) 290 | cg.sb = append(cg.sb, ',', '\n') 291 | } 292 | iend_prev = endOf(&nodes[i]) 293 | if kind == AstBlock { 294 | cg.generateForC(nodes[i].nodes) 295 | } else { 296 | cg.sb = append(cg.sb, "{obj: ("...) 297 | cg.generateForJs(nodes[i].nodes[1 : len(nodes[i].nodes)-1]) 298 | cg.sb = append(cg.sb, ')', '}', ',', '\n') 299 | } 300 | } 301 | iend := endOf(&nodes[len(nodes)-1]) 302 | if iend_prev != iend { 303 | cg.sb = pushStringLiteral( 304 | cg.input[iend_prev:iend], 305 | cg.sb, 306 | ) 307 | cg.sb = append(cg.sb, ',', '\n') 308 | } 309 | } 310 | 311 | func (cg *Codegen) generateForJs(nodes []AstNode) { 312 | iend_prev := startOf(&nodes[0]) 313 | for i := range nodes { 314 | kind := nodes[i].token.kind 315 | if kind > AstEnd { 316 | continue 317 | } 318 | istart := startOf(&nodes[i]) 319 | if iend_prev != istart { 320 | cg.sb = append(cg.sb, 321 | cg.input[iend_prev:istart]..., 322 | ) 323 | } 324 | iend_prev = endOf(&nodes[i]) 325 | if kind == AstBlock { 326 | cg.generateForJs(nodes[i].nodes) 327 | } else if kind == AstMacro { 328 | cg.sb = append(cg.sb, "\nJC_INTERNAL_TOPLEVEL.push(new JcInternalQuote([\n"...) 329 | cg.generateForC(nodes[i].nodes) 330 | cg.sb = append(cg.sb, "\n\"\\n\"\n]));\n"...) 331 | } else if kind == AstLfnArgs { 332 | cg.sb = append(cg.sb, "new JcInternalFunction(\nnew JcInternalQuote([\n"...) 333 | cg.generateForC(nodes[i].nodes) 334 | cg.sb = append(cg.sb, "]),\nnew JcInternalQuote([\n"...) 335 | iend_prev = startOf(&nodes[i+1]) 336 | } else if kind == AstLfnRetType { 337 | cg.generateForC(nodes[i].nodes) 338 | cg.sb = append(cg.sb, "]),\nnew JcInternalQuote([\n"...) 339 | } else if kind == AstLfnBody { 340 | cg.generateForC(nodes[i].nodes) 341 | cg.sb = append(cg.sb, "]))"...) 342 | } else if kind == AstTicklit { 343 | tnodes := nodes[i].nodes 344 | for j := range tnodes { 345 | kind = tnodes[j].token.kind 346 | if kind == AstInterpol { 347 | cg.generateForJs(tnodes[j].nodes) 348 | } else { 349 | cg.sb = append(cg.sb, cg.input[tnodes[j].token.begin:tnodes[j].token.end]...) 350 | } 351 | } 352 | } else if kind == AstQuote { 353 | qnodes := nodes[i].nodes 354 | if len(qnodes) == 3 { 355 | cg.sb = append(cg.sb, "new JcInternalQuote([])"...) 356 | } else { 357 | cg.sb = append(cg.sb, "new JcInternalQuote([\n"...) 358 | lparen_end := qnodes[1].token.end 359 | quote_start := startOf(&qnodes[2]) 360 | if lparen_end < quote_start { 361 | cg.sb = pushStringLiteral(cg.input[lparen_end:quote_start], cg.sb) 362 | cg.sb = append(cg.sb, ",\n"...) 363 | } 364 | cg.generateForC(qnodes[2:len(qnodes)-1]) 365 | quote_end := endOf(&qnodes[len(qnodes)-2]) 366 | rparen_start := qnodes[len(qnodes)-1].token.begin 367 | if quote_end < rparen_start { 368 | cg.sb = pushStringLiteral(cg.input[quote_end:rparen_start], cg.sb) 369 | cg.sb = append(cg.sb, "\n])"...) 370 | } else { 371 | cg.sb = append(cg.sb, "])"...) 372 | } 373 | } 374 | } 375 | } 376 | iend := endOf(&nodes[len(nodes)-1]) 377 | if iend_prev != iend { 378 | cg.sb = append(cg.sb, cg.input[iend_prev:iend]...) 379 | } 380 | } 381 | 382 | func codegenPerform(root *AstNode, input []byte, outfile string) []byte { 383 | cg := Codegen{ 384 | sb: make([]byte, 0, 16384), 385 | input: input, 386 | } 387 | cg.sb = append(cg.sb, "const JC_INTERNAL_OUTFILE = "...) 388 | cg.sb = pushStringLiteral([]byte(outfile), cg.sb) 389 | cg.sb = append(cg.sb, ";\n"...) 390 | cg.sb = append(cg.sb, js_prelude...) 391 | cg.generateForJs(root.nodes) 392 | cg.sb = append(cg.sb, "\njcInternalFinish();\n"...) 393 | return cg.sb 394 | } 395 | -------------------------------------------------------------------------------- /src/parser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "github.com/evanw/esbuild/pkg/api" 7 | ) 8 | 9 | type ParserLexer struct { 10 | result AstNode 11 | input []byte 12 | err string 13 | //parser_ref astParser 14 | cursor uint 15 | prev_tok uint32 16 | block_level uint32 17 | in_ticks bool 18 | in_macro bool 19 | } 20 | 21 | func plNew(input []byte /*, parser_ref astParser*/) ParserLexer { 22 | return ParserLexer{ 23 | result: AstNode{LexToken{0, 0, 0}, nil}, 24 | input: input, 25 | err: "", 26 | //parser_ref: parser_ref, 27 | cursor: 0, 28 | prev_tok: 0, 29 | block_level: 0, 30 | in_ticks: false, 31 | in_macro: false, 32 | } 33 | } 34 | 35 | /* 36 | func (pl *ParserLexer) ensureNoLookahead(ctx string) { 37 | yychar := pl.parser_ref.Lookahead() 38 | if yychar != -1 { 39 | fmt.Printf("parser looked ahead "+ctx+" and found %d, this should not happen\n", yychar) 40 | os.Exit(1) 41 | } 42 | }*/ 43 | 44 | /* 45 | once an lbrace, rbrace, start btick, or end btick 46 | 47 | appears in one of these respective situations, 48 | it should be immediately used to reduce. 49 | */ 50 | func (pl *ParserLexer) beginBlock() { 51 | //pl.ensureNoLookahead("into block") 52 | pl.block_level++ 53 | } 54 | func (pl *ParserLexer) endBlock() { 55 | //pl.ensureNoLookahead("after block") 56 | if pl.block_level != 0 { 57 | pl.block_level-- 58 | } 59 | } 60 | func (pl *ParserLexer) inTicks() { 61 | //pl.ensureNoLookahead("into tick literal") 62 | pl.in_ticks = true 63 | } 64 | func (pl *ParserLexer) outTicks() { 65 | //pl.ensureNoLookahead("past tick literal") 66 | pl.in_ticks = false 67 | } 68 | 69 | func (pl *ParserLexer) receiveResult(result AstNode) { 70 | pl.result = result 71 | } 72 | func (pl *ParserLexer) Lex(lval *astSymType) int { 73 | var token LexToken 74 | for { 75 | if pl.in_ticks { 76 | token = lexTickLit(pl.input, pl.cursor) 77 | } else { 78 | token = lexNextToken(pl.input, pl.cursor) 79 | } 80 | if token.kind == TEof { 81 | if pl.in_macro { 82 | pl.in_macro = false 83 | lval.node = AstNode{LexToken{ 84 | uint32(pl.cursor), uint32(pl.cursor), TMacroEnd, 85 | }, nil} 86 | return TMacroEnd 87 | } 88 | return 0 89 | } else if token.kind == TInvalid { 90 | pl.err = "invalid token" 91 | return 0 92 | } else if pl.in_ticks { 93 | pl.cursor = uint(token.end) 94 | lval.node = AstNode{token, nil} 95 | return int(token.kind) 96 | } else if token.kind == TPLSlash && 97 | pl.prev_tok != TPLInc && 98 | pl.prev_tok != TPLDec && 99 | pl.prev_tok != TPLValue && 100 | pl.prev_tok != TRBrace && 101 | pl.prev_tok != TRCircle && 102 | pl.prev_tok != TRSquare && 103 | pl.prev_tok != TIdentifier { 104 | token = lexRegexpLit(pl.input, uint(token.begin)) 105 | pl.prev_tok = TPLValue 106 | pl.cursor = uint(token.end) 107 | lval.node = AstNode{token, nil} 108 | return int(token.kind) 109 | } else if token.kind == TPLPound && 110 | (pl.prev_tok == TPLNewline || pl.prev_tok == 0) && 111 | pl.block_level == 0 && 112 | !pl.in_macro { 113 | pl.prev_tok = TPLPound 114 | pl.cursor = uint(token.end) 115 | token.kind = TMacroStart 116 | lval.node = AstNode{token, nil} 117 | pl.in_macro = true 118 | return TMacroStart 119 | } 120 | pl.cursor = uint(token.end) 121 | pl.prev_tok = token.kind 122 | if token.kind == TPLNewline { 123 | if pl.in_macro { 124 | token.kind = TMacroEnd 125 | pl.in_macro = false 126 | } else { 127 | continue 128 | } 129 | } else if token.kind > TPLBefore && 130 | token.kind < TPLAfter { 131 | token.kind = TOther 132 | } 133 | lval.node = AstNode{token, nil} 134 | return int(token.kind) 135 | } 136 | } 137 | func (pl *ParserLexer) Error(s string) { 138 | pl.err = s 139 | } 140 | 141 | func parseInitialAst(input []byte) (AstNode, string) { 142 | parser := astNewParser() 143 | pl := plNew(input /*, parser*/) 144 | result := parser.Parse(&pl) 145 | if pl.err == "" && result != 0 { 146 | pl.err = "got result " + strconv.Itoa(result) 147 | } 148 | if pl.err != "" { 149 | return AstNode{LexToken{0, 0, 0}, nil}, pl.err 150 | } else { 151 | return pl.result, "" 152 | } 153 | } 154 | 155 | const U32_MAX uint32 = 4294967295 156 | 157 | func isIndisputablyJs(stmt []AstNode) bool { 158 | ch1 := stmt[0].token.kind 159 | /* C declaration can't start with special characters 160 | (we allow '[' for C23 attributes) 161 | */ 162 | if ch1 == TLSquare { 163 | if len(stmt) < 2 { 164 | return true 165 | } 166 | ch1 = stmt[1].token.kind 167 | if ch1 != TLSquare { 168 | return true 169 | } 170 | } else if ch1 != TConst && 171 | ch1 != TStructUnionEnumClass && 172 | ch1 != TIdentifier && 173 | 174 | ch1 != AstInterpol { 175 | return true 176 | } 177 | if len(stmt) >= 2 { 178 | ch2 := stmt[1].token.kind 179 | /* object destructuring */ 180 | if (ch1 == TConst || ch1 == TLCircle) && 181 | ch2 == TLBrace { 182 | return true 183 | } 184 | /* "const identifier =" */ 185 | if len(stmt) >= 3 { 186 | ch3 := stmt[2].token.kind 187 | if ch1 == TConst && 188 | ch2 == TIdentifier && 189 | ch3 == TEq { 190 | return true 191 | } 192 | } 193 | } 194 | return false 195 | } 196 | 197 | func getJsBlockStmtEnd(nodes []AstNode) uint32 { 198 | var ch uint32 199 | var circle_nesting uint32 = 0 200 | for i := range nodes { 201 | ch = nodes[i].token.kind 202 | if ch == TLCircle { 203 | circle_nesting++ 204 | } else if ch == TRCircle { 205 | if circle_nesting > 0 { 206 | circle_nesting-- 207 | } 208 | } else if ch == AstBlock && 209 | circle_nesting == 0 { 210 | return uint32(i + 1) 211 | } 212 | } 213 | return U32_MAX 214 | } 215 | 216 | func nextMaybeJsStmt(nodes []AstNode) (uint32, uint32) { 217 | var ch uint32 218 | var offset uint32 = 0 219 | for { 220 | found_meat := false 221 | for i := range nodes { 222 | ch = nodes[i].token.kind 223 | if ch != AstBlock && ch != AstMacro && 224 | ch != TSemi { 225 | found_meat = true 226 | nodes = nodes[i:] 227 | offset += uint32(i) 228 | break 229 | } 230 | } 231 | /*fmt.Println("\nFOUND MEAT!!!!!!") 232 | debugNode(&AstNode{ 233 | token: LexToken{0, 1, AstBlock}, 234 | nodes: nodes, 235 | }, input) 236 | fmt.Println("\nMEAT END !!!!!!")*/ 237 | if !found_meat || len(nodes) < 2 { 238 | return U32_MAX, U32_MAX 239 | } 240 | ch = nodes[0].token.kind 241 | if ch == TFunctionForWhileJsclass || 242 | nodes[1].token.kind == TFunctionForWhileJsclass { 243 | jend := getJsBlockStmtEnd(nodes) 244 | if jend == U32_MAX { 245 | //fmt.Println("BAD JEND!!!!") 246 | return U32_MAX, U32_MAX 247 | } 248 | nodes = nodes[jend:] 249 | offset += jend 250 | continue 251 | } 252 | var stmt_end uint32 = uint32(len(nodes)) 253 | for i := range nodes { 254 | ch = nodes[i].token.kind 255 | if ch == TSemi { 256 | stmt_end = uint32(i + 1) 257 | break 258 | } 259 | } 260 | if isIndisputablyJs(nodes[0:stmt_end]) { 261 | //fmt.Println("BUT THE MEAT IS JS!!!!") 262 | nodes = nodes[stmt_end:] 263 | offset += stmt_end 264 | continue 265 | } 266 | return offset, offset + stmt_end 267 | } 268 | } 269 | 270 | func startOf(node *AstNode) uint32 { 271 | for node.token.kind < AstEnd { 272 | node = &node.nodes[0] 273 | } 274 | return node.token.begin 275 | } 276 | 277 | func endOf(node *AstNode) uint32 { 278 | for node.token.kind < AstEnd { 279 | node = &node.nodes[len(node.nodes)-1] 280 | } 281 | return node.token.end 282 | } 283 | 284 | /* 285 | We don't allow unnecessary parentheses 286 | 287 | in C declarations as they create ambiguity. 288 | For example: 289 | 290 | "console(logger[5])[10] = value" 291 | 292 | JS -> console fn is called with value of logger array at index 5, 293 | 294 | then the item at index 10 of the returned array is assigned to value 295 | 296 | C -> logger is an array console[5][10], it is assigned to value 297 | 298 | which is a macro that expands to an initializer list 299 | 300 | In contrast, "console logger[5][10] = value" is unambiguously C, 301 | and so is "console (*logger[5])[10] = value". 302 | */ 303 | func isValidJs(js_maybe []AstNode, input []byte) bool { 304 | /* cheap trick */ 305 | tokens_builder := strings.Builder{} 306 | for i := range js_maybe { 307 | node := &js_maybe[i] 308 | _, _ = tokens_builder.Write(input[startOf(node):endOf(node)]) 309 | _ = tokens_builder.WriteByte(' ') 310 | } 311 | tokens := tokens_builder.String() 312 | options := api.TransformOptions{} 313 | result := api.Transform(tokens, options) 314 | return len(result.Errors) == 0 315 | } 316 | 317 | func tryExtractC(nodes []AstNode, input []byte) uint32 { 318 | var ch uint32 319 | var stmt_end uint32 = uint32(len(nodes)) 320 | var circle_nesting uint32 = 0 321 | has_circles := false 322 | found_seu := false 323 | for i := range nodes { 324 | ch = nodes[i].token.kind 325 | /* has a JS keyword */ 326 | if ch == TJsOnly { 327 | return U32_MAX 328 | } else if ch == AstMacro { 329 | return uint32(i) 330 | } else if ch == TLCircle { 331 | has_circles = true 332 | circle_nesting++ 333 | } else if ch == TRCircle { 334 | if circle_nesting > 0 { 335 | circle_nesting-- 336 | } else { 337 | return U32_MAX 338 | } 339 | } else if ch == TStructUnionEnumClass { 340 | found_seu = true 341 | } else if ch == AstBlock && 342 | circle_nesting == 0 { 343 | // consume possible trailing semicolon post-block 344 | // const int values[1] = {0}; 345 | // (nodes will always be semicolon-terminated) 346 | if (i+2) == len(nodes) { 347 | break 348 | } else { 349 | stmt_end = uint32(i+1) 350 | break 351 | } 352 | } 353 | } 354 | /* we don't allow struct declarations with parentheses 355 | as they are ambiguous: 356 | - valid struct declaration 357 | struct identifier macro(identifier) { ... } 358 | - valid function declaration 359 | struct identifier name(arg_list) { ... } 360 | because macros can be generated dynamically, 361 | we cannot tell the difference. 362 | */ 363 | if found_seu && !has_circles { 364 | return uint32(len(nodes)) 365 | } 366 | 367 | stmt := nodes[:stmt_end] 368 | for i := range stmt { 369 | ch = stmt[i].token.kind 370 | /* has interpolated JS, 371 | or struct/enum/union keyword */ 372 | if ch == AstInterpol || 373 | ch == TStructUnionEnumClass { 374 | return stmt_end 375 | } 376 | } 377 | for i := range stmt { 378 | ch = stmt[i].token.kind 379 | if ch == TEq { 380 | break 381 | /* dot operator in type specifier 382 | or declarator impossible */ 383 | } else if ch == '.' { 384 | return U32_MAX 385 | } 386 | } 387 | if isValidJs(nodes, input) { 388 | return U32_MAX 389 | } 390 | return stmt_end 391 | } 392 | 393 | func nextCSection(root []AstNode, input []byte) (uint32, uint32) { 394 | var offset uint32 = 0 395 | for { 396 | jstart, jend := nextMaybeJsStmt(root) 397 | //fmt.Printf("jstart is %d\n", jstart) 398 | if jstart == U32_MAX { 399 | return U32_MAX, U32_MAX 400 | } 401 | cend := tryExtractC(root[jstart:jend], input) 402 | if cend == U32_MAX { 403 | root = root[jend:] 404 | offset += jend 405 | continue 406 | } 407 | 408 | return offset + jstart, offset + jstart + cend 409 | } 410 | } 411 | 412 | /* 413 | func getBeginOf(node *AstNode) uint32 { 414 | for node.token.kind < AstEnd { 415 | node = &node.nodes[0] 416 | } 417 | return node.token.begin 418 | } 419 | 420 | func getEndOf(node *AstNode) uint32 { 421 | for node.token.kind < AstEnd { 422 | node = &node.nodes[len(node.nodes)-1] 423 | } 424 | return node.token.end 425 | }*/ 426 | 427 | func reviseTLC(root []AstNode, input []byte) []AstNode { 428 | new_root := make([]AstNode, 0, 4) 429 | for { 430 | cstart, cend := nextCSection(root, input) 431 | if cstart == U32_MAX { 432 | break 433 | } else { 434 | //fmt.Printf("c section is [%d, %d)\n", cstart, cend) 435 | } 436 | new_root = append(new_root, root[0:cstart]...) 437 | new_root = append(new_root, AstNode{ 438 | token: LexToken{ 439 | begin: 0, 440 | end: 0, 441 | kind: AstMacro, 442 | }, 443 | nodes: root[cstart:cend], 444 | }) 445 | root = root[cend:] 446 | } 447 | new_root = append(new_root, root...) 448 | return new_root 449 | } 450 | 451 | func findLastLfn(nodes []AstNode) (uint32, uint32, uint32) { 452 | var prev uint32 = 0 453 | var cur uint32 = 0 454 | for i := len(nodes) - 1; i >= 0; i-- { 455 | cur = prev 456 | prev = nodes[i].token.kind 457 | if prev == TRCircle && 458 | cur == TMinusGt { 459 | var circle_nesting uint32 = 1 460 | mgt := i + 1 461 | for i--; i >= 0; i-- { 462 | var kind uint32 = nodes[i].token.kind 463 | if kind == TRCircle { 464 | circle_nesting++ 465 | } else if kind == TLCircle { 466 | circle_nesting-- 467 | if circle_nesting == 0 { 468 | break 469 | } 470 | } 471 | } 472 | if circle_nesting != 0 { 473 | break 474 | } 475 | start := i 476 | circle_nesting = 0 477 | found_block := false 478 | for i = mgt + 1; i < len(nodes); i++ { 479 | var kind uint32 = nodes[i].token.kind 480 | if kind == TLCircle { 481 | circle_nesting++ 482 | } else if kind == TRCircle { 483 | if circle_nesting == 0 { 484 | break 485 | } 486 | circle_nesting-- 487 | } else if kind == AstBlock && 488 | (mgt+1) != i { 489 | found_block = true 490 | break 491 | } 492 | } 493 | if !found_block { 494 | i = start 495 | continue 496 | } 497 | return uint32(start), uint32(mgt), uint32(i + 1) 498 | } 499 | } 500 | return U32_MAX, U32_MAX, U32_MAX 501 | } 502 | 503 | func replaceLfnMaybe(root []AstNode) []AstNode { 504 | type Lfn struct { 505 | start, mgt, end uint32 506 | } 507 | lfn_locs := make([]Lfn, 0, 4) 508 | nodes := root 509 | for { 510 | lstart, lmgt, lend := findLastLfn(nodes) 511 | if lstart == U32_MAX { 512 | break 513 | } 514 | lfn_locs = append(lfn_locs, Lfn{lstart, lmgt, lend}) 515 | nodes = nodes[0:lstart] 516 | } 517 | if len(lfn_locs) == 0 { 518 | return nil 519 | } 520 | replaced := make([]AstNode, 0, len(root)-(3*len(lfn_locs))) 521 | var prev_end uint32 = 0 522 | for i := len(lfn_locs) - 1; i >= 0; i-- { 523 | l := lfn_locs[i] 524 | replaced = append(replaced, root[prev_end:l.start]...) 525 | replaced = append(replaced, AstNode{ 526 | token: LexToken{0, 0, AstLfnArgs}, 527 | nodes: root[l.start:l.mgt], 528 | }) 529 | replaced = append(replaced, AstNode{ 530 | token: LexToken{0, 0, AstLfnRetType}, 531 | nodes: root[l.mgt+1 : l.end-1], 532 | }) 533 | body := root[l.end-1] 534 | body.token.kind = AstLfnBody 535 | replaced = append(replaced, body) 536 | prev_end = l.end 537 | } 538 | replaced = append(replaced, root[prev_end:]...) 539 | return replaced 540 | } 541 | 542 | func reviseWithinLfn(root []AstNode) { 543 | for i := range root { 544 | kind := root[i].token.kind 545 | if kind == AstBlock { 546 | reviseWithinLfn(root[i].nodes) 547 | } else if kind == AstInterpol { 548 | replaced := reviseLfn(root[i].nodes) 549 | if replaced != nil { 550 | root[i].nodes = replaced 551 | } 552 | } else { 553 | return 554 | } 555 | } 556 | } 557 | 558 | func reviseLfn(root []AstNode) []AstNode { 559 | replaced := replaceLfnMaybe(root) 560 | changed := false 561 | if replaced != nil { 562 | changed = true 563 | root = replaced 564 | } 565 | for i := range root { 566 | kind := root[i].token.kind 567 | if kind > AstEnd { 568 | continue 569 | } 570 | if kind == AstLfnArgs || 571 | kind == AstLfnRetType || 572 | kind == AstMacro || 573 | kind == AstQuote { 574 | reviseWithinLfn(root[i].nodes) 575 | } else { 576 | replaced = reviseLfn(root[i].nodes) 577 | if replaced != nil { 578 | root[i].nodes = replaced 579 | } 580 | } 581 | } 582 | if changed { 583 | return root 584 | } else { 585 | return nil 586 | } 587 | } 588 | 589 | func parseIntoAst(input []byte) (AstNode, string) { 590 | root, estr := parseInitialAst(input) 591 | if estr != "" { 592 | return AstNode{LexToken{0, 0, 0}, nil}, estr 593 | } 594 | root.nodes = reviseTLC(root.nodes, input) 595 | revised := reviseLfn(root.nodes) 596 | if revised != nil { 597 | root.nodes = revised 598 | } 599 | return root, "" 600 | } 601 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jc 2 | `jc` is a tool that enables the meta-programming of C/C++ with JavaScript. 3 | 4 | ```cpp 5 | // Specialize exponentiation for a power 6 | function powFor(pwr) { 7 | // A quote is a piece of C/C++ code expressed as a JS value. 8 | var expr = quote(1); 9 | // Add " * v" to the expression `pwr` times. 10 | for (var i = 0; i < pwr; i++) { 11 | expr.add(quote( * v)); 12 | } 13 | 14 | // Create a C/C++ function as a JS value. 15 | const pow_fn = (int v) -> static int { 16 | // JS interpolation: insert a JS value into C/C++ code. 17 | return ${expr}; 18 | }; 19 | // Return it. 20 | return pow_fn; 21 | } 22 | 23 | const pow7 = powFor(7); 24 | /* 25 | static int jc_lfn1(int v) { 26 | return 1 * v * v * v * v * v * v * v; 27 | } 28 | */ 29 | 30 | #include 31 | int main(void) { 32 | printf("got %d\n", ${pow7}(2)); 33 | return 0; 34 | } 35 | ``` 36 | ```sh 37 | > jc -i example.jc -o example.c -n node && gcc example.c && ./a.out 38 | got 128 39 | ``` 40 | # Examples 41 | ## Compute static data at compile-time 42 | ```cpp 43 | var fib = []; 44 | var prev = 0; 45 | var cur = 1; 46 | for (var i = 0; i < 40; i++) { 47 | var next = prev + cur; 48 | fib.push(cur); 49 | prev = cur; 50 | cur = next; 51 | } 52 | 53 | const int FIB_SEQ[${fib.length}] = ${fib}; 54 | #include 55 | int main(void) { 56 | for (int i = 0; i < ${fib.length}; i++) { 57 | printf("%d ", FIB_SEQ[i]); 58 | } 59 | return 0; 60 | } 61 | ``` 62 | ``` 63 | 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 64 | ``` 65 | 66 | ## Compile domain-specific languages 67 | ```cpp 68 | #include 69 | #include 70 | 71 | function compileBf(code, tapesz) { 72 | var body = quote(); 73 | var loopstack = []; 74 | for (const ch of code) { 75 | switch (ch) { 76 | case '>': 77 | body.add(quote( 78 | i++; 79 | )); 80 | break; 81 | case '<': 82 | body.add(quote( 83 | i--; 84 | )); 85 | break; 86 | case '+': 87 | body.add(quote( 88 | tape[i]++; 89 | )); 90 | break; 91 | case '-': 92 | body.add(quote( 93 | tape[i]--; 94 | )); 95 | break; 96 | case '.': 97 | body.add(quote( 98 | putchar(tape[i]); 99 | )); 100 | break; 101 | case ',': 102 | body.add(quote( 103 | tape[i] = getchar(); 104 | )); 105 | break; 106 | case '[': 107 | // gensym() returns a unique symbol 108 | var loop = { 109 | before: gensym(), 110 | after: gensym(), 111 | }; 112 | loopstack.push(loop); 113 | body.add(quote( 114 | ${loop.before}: 115 | if (tape[i] == 0) { 116 | goto ${loop.after}; 117 | } 118 | )); 119 | break; 120 | case ']': 121 | var loop = loopstack.pop(); 122 | if (!loop) { 123 | throw new Error("unmatched loop end"); 124 | } 125 | body.add(quote( 126 | goto ${loop.before}; 127 | ${loop.after}: 128 | ; 129 | )); 130 | break; 131 | } 132 | } 133 | if (loopstack.length > 0) { 134 | throw new Error("unmatched loop start"); 135 | } 136 | return (void) -> static void { 137 | int tape[${tapesz}]; 138 | int i = 0; 139 | memset(tape, 0, sizeof(tape)); 140 | ${body} 141 | }; 142 | } 143 | 144 | const add3 = compileBf("+[,+++.]", 10); 145 | /* 146 | static void jc_lfn1(void) { 147 | int tape[10]; 148 | int i = 0; 149 | memset(tape, 0, sizeof(tape)); 150 | tape[i]++; 151 | jc_sym1: 152 | if (tape[i] == 0) { 153 | goto jc_sym2; 154 | } 155 | tape[i] = getchar(); 156 | tape[i]++; 157 | tape[i]++; 158 | tape[i]++; 159 | putchar(tape[i]); 160 | goto jc_sym1; 161 | jc_sym2: 162 | ; 163 | } 164 | */ 165 | 166 | int main(void) { 167 | ${add3}(); 168 | } 169 | ``` 170 | ``` 171 | > a 172 | d 173 | > w 174 | z 175 | ``` 176 | 177 | ## Compile-time `printf` 178 | ```cpp 179 | #include 180 | #include 181 | var mkprintf_memory = {}; 182 | function mkprintf(fmt) { 183 | if (mkprintf_memory[fmt]) { 184 | return mkprintf_memory[fmt]; 185 | } 186 | var specifiers = []; 187 | var start_index = 0; 188 | var in_spec = false; 189 | for (var i = 0; i < fmt.length; i++) { 190 | const ch = fmt[i]; 191 | if (!in_spec) { 192 | if (ch == '%') { 193 | const literal = fmt.substring( 194 | start_index, i 195 | ); 196 | specifiers.push(["lit", literal]); 197 | in_spec = true; 198 | } 199 | continue; 200 | } 201 | switch (ch) { 202 | case '%': 203 | specifiers.push(["lit", "%"]); 204 | break; 205 | case 's': 206 | specifiers.push(["str"]); 207 | break; 208 | case 'd': 209 | specifiers.push(["num"]); 210 | break; 211 | default: 212 | throw new Error("fmt specifier "+ch+" unimplemented"); 213 | } 214 | start_index = i+1; 215 | in_spec = false; 216 | } 217 | if (start_index != fmt.length) { 218 | const literal = fmt.substring( 219 | start_index 220 | ); 221 | specifiers.push(["lit", literal]); 222 | } 223 | var args = quote(); 224 | var first_arg = true; 225 | var body = quote(); 226 | for (var spec of specifiers) { 227 | var kind = spec[0]; 228 | if (kind == "lit") { 229 | const literal = spec[1]; 230 | body.add(quote( 231 | fwrite(${literal}, 1, ${literal.length}, stdout); 232 | )); 233 | } else if (kind == "num") { 234 | const arg = gensym(); 235 | if (!first_arg) { 236 | args.add(quote(, )); 237 | } else { 238 | first_arg = false; 239 | } 240 | args.add(quote(int ${arg})); 241 | body.add(quote({ 242 | char buf[32]; 243 | int idx = sizeof(buf); 244 | int neg = ${arg} < 0; 245 | if (neg) { 246 | ${arg} = -${arg}; 247 | } 248 | do { 249 | buf[--idx] = '0'+(${arg} % 10); 250 | ${arg} /= 10; 251 | } while (${arg} > 0); 252 | if (neg) { 253 | buf[--idx] = '-'; 254 | } 255 | fwrite(buf+idx, 1, sizeof(buf)-idx, stdout); 256 | })); 257 | } else if (kind == "str") { 258 | if (!first_arg) { 259 | args.add(quote(, )); 260 | } else { 261 | first_arg = false; 262 | } 263 | const arg = gensym(); 264 | args.add(quote(const char* ${arg})); 265 | body.add(quote( 266 | fwrite(${arg}, 1, strlen(${arg}), stdout); 267 | )); 268 | } else { 269 | throw new Error("internal mkprintf error"); 270 | } 271 | } 272 | const printfn = (${args}) -> static void { 273 | ${body} 274 | }; 275 | mkprintf_memory[fmt] = printfn; 276 | return printfn; 277 | } 278 | 279 | int main(void) { 280 | ${mkprintf("Hello, %s!\n")}("world"); 281 | ${mkprintf("%s has %d %s.\n")}("He", 20, "stones"); 282 | } 283 | ``` 284 | ``` 285 | Hello, world! 286 | He has 20 stones. 287 | ``` 288 | 289 | ## Compile-time regular expressions 290 | ```cpp 291 | const spawn = require("child_process").spawn; 292 | var mkmatches_memory = {}; 293 | function mkmatches(reg) { 294 | if (mkmatches_memory[reg]) { 295 | return mkmatches_memory[reg]; 296 | } 297 | /* by using promises, we ensure 298 | mkmatches calls will be 299 | executed concurrently by the JS 300 | event loop during compilation */ 301 | const promise = new Promise((resolve, reject) => { 302 | const re2c = spawn("re2c", ["-"]); 303 | var output = ""; 304 | var errmsg = ""; 305 | re2c.on("error", function(err) { 306 | reject("can't spawn re2c: "+err); 307 | }); 308 | re2c.stdin.end(` 309 | /*!re2c 310 | re2c:yyfill:enable = 0; 311 | re2c:define:YYCTYPE = char; 312 | 313 | * { return 0; } 314 | ${reg} { return 1; } 315 | */ 316 | `); 317 | re2c.stdout.on("data", function(chunk) { 318 | output += chunk; 319 | }); 320 | re2c.stderr.on("data", function(chunk) { 321 | errmsg += chunk; 322 | }); 323 | re2c.on("close", function(code) { 324 | if (code !== 0) { 325 | reject( 326 | "re2c exited with code " 327 | +code+" and msg "+errmsg 328 | ); 329 | return; 330 | } 331 | resolve((const char* input) -> static int { 332 | const char* YYCURSOR = input; 333 | const char* YYMARKER; 334 | // convert a string to a quote 335 | ${strtoq(output)} 336 | }); 337 | }); 338 | }); 339 | mkmatches_memory[reg] = promise; 340 | return promise; 341 | } 342 | 343 | #include 344 | #define matches1 ${mkmatches(`[A-Z]+ "test"`)} 345 | #define matches2 ${mkmatches(`[a-z]+ "TEST"`)} 346 | 347 | int main(void) { 348 | printf("regex 1: %d %d\n", 349 | matches1("hellotest"), 350 | matches1("HELLOtest") 351 | ); 352 | printf("regex 2: %d %d\n", 353 | matches2("goodbyeTEST"), 354 | matches2("goodbyeTESt") 355 | ); 356 | } 357 | ``` 358 | ``` 359 | regex 1: 0 1 360 | regex 2: 1 0 361 | ``` 362 | 363 | ## Generics in C 364 | ```cpp 365 | #include 366 | function mkvec(T) { 367 | return quote( 368 | typedef struct { 369 | ${T}* ptr; 370 | long len, cap; 371 | } vec_${T}; 372 | static void vec_init_${T}(vec_${T}* v) { 373 | v->ptr = NULL; 374 | v->len = v->cap = 0; 375 | } 376 | static void vec_push_back_${T}(vec_${T}* v, ${T} val) { 377 | if (v->len == v->cap) { 378 | if (v->cap == 0) { 379 | v->cap = 1; 380 | } else { 381 | v->cap *= 2; 382 | } 383 | v->ptr = realloc(v->ptr, v->cap); 384 | } 385 | v->ptr[v->len++] = val; 386 | } 387 | static ${T} vec_pop_back_${T}(vec_${T}* v) { 388 | return v->ptr[--v->len]; 389 | } 390 | static ${T} vec_pop_front_${T}(vec_${T}* v) { 391 | ${T} item = v->ptr[0]; 392 | for (long i = 1; i < v->len; i++) { 393 | v->ptr[i-1] = v->ptr[i]; 394 | } 395 | v->len--; 396 | return item; 397 | } 398 | ); 399 | } 400 | 401 | ${mkvec(quote(int))}; 402 | 403 | #include 404 | int main(void) { 405 | vec_int v; 406 | vec_init_int(&v); 407 | vec_push_back_int(&v, 5); 408 | vec_push_back_int(&v, 10); 409 | vec_push_back_int(&v, 15); 410 | int a = vec_pop_back_int(&v); 411 | int b = vec_pop_front_int(&v); 412 | int c = vec_pop_back_int(&v); 413 | printf("%d %d %d\n", a, b, c); 414 | } 415 | ``` 416 | ``` 417 | 15 5 10 418 | ``` 419 | 420 | ## Conditional compilation 421 | ```cpp 422 | 423 | const util = require("util"); 424 | const execFile = util.promisify( 425 | require("child_process").execFile 426 | ); 427 | const fs = require("fs/promises"); 428 | const cc = (async() => { 429 | if (process.env.CC) { 430 | return process.env.CC; 431 | } 432 | try { 433 | await execFile("gcc", ["-v"]); 434 | return "gcc"; 435 | } catch (_err) {} 436 | try { 437 | await execFile("clang", ["-v"]); 438 | return "clang"; 439 | } catch (_err) {} 440 | try { 441 | await execFile("cc", ["-v"]); 442 | return "cc"; 443 | } catch (_err) {} 444 | throw new Error("can't find C compiler:"+ 445 | "supply one by setting env variable CC"); 446 | })(); 447 | const has_std_atomics = (async() => { 448 | const tmpdir = await fs.mkdtemp("atomic"); 449 | const tmpfile = tmpdir+"/test.c"; 450 | await fs.writeFile(tmpfile, ` 451 | #include 452 | int main(void) { 453 | _Atomic(int) _a; 454 | return 0; 455 | } 456 | `); 457 | var supported = true; 458 | try { 459 | await execFile(await cc, [ 460 | tmpfile, "-o", 461 | tmpdir+"/a.out", "-O0" 462 | ]); 463 | } catch (_err) { 464 | supported = false; 465 | } 466 | await fs.rm(tmpdir, {recursive: true, force: true}); 467 | return supported; 468 | })(); 469 | 470 | ${(async() => { 471 | if (await has_std_atomics) { 472 | return quote( 473 | #include 474 | #define Relaxed memory_order_relaxed 475 | #define Acquire memory_order_acquire 476 | #define Release memory_order_release 477 | #define AcqRel memory_order_acq_rel 478 | #define SeqCst memory_order_seq_cst 479 | ); 480 | } else { 481 | return quote( 482 | #define Relaxed __ATOMIC_RELAXED 483 | #define Acquire __ATOMIC_ACQUIRE 484 | #define Release __ATOMIC_RELEASE 485 | #define AcqRel __ATOMIC_ACQ_REL 486 | #define SeqCst __ATOMIC_SEQ_CST 487 | ); 488 | } 489 | })()}; 490 | 491 | function atomicStdFor(T) { 492 | return quote( 493 | typedef struct { 494 | _Atomic(${T}) repr; 495 | } atomic_${T}; 496 | static inline atomic_${T} atomic_new_${T}( 497 | ${T} v 498 | ) { 499 | atomic_${T} a; 500 | #ifdef atomic_init 501 | atomic_init(&a.repr, v); 502 | #else 503 | a.repr = ATOMIC_VAR_INIT(v); 504 | #endif 505 | return a; 506 | } 507 | static inline ${T} atomic_load_${T}( 508 | const atomic_${T}* a, 509 | memory_order order 510 | ) { 511 | atomic_${T}* a_nonconst = (atomic_${T}*)a; 512 | return atomic_load_explicit( 513 | &a_nonconst->repr, order 514 | ); 515 | } 516 | static inline void atomic_store_${T}( 517 | atomic_${T}* a, ${T} v, 518 | memory_order order 519 | ) { 520 | return atomic_store_explicit( 521 | &a->repr, v, order 522 | ); 523 | } 524 | /* ... */ 525 | ); 526 | } 527 | 528 | function atomicGccFor(T) { 529 | return quote( 530 | typedef struct { 531 | ${T} repr; 532 | } atomic_${T}; 533 | static inline atomic_${T} atomic_new_${T}( 534 | ${T} v 535 | ) { 536 | atomic_${T} a; 537 | a.repr = v; 538 | return a; 539 | } 540 | static inline ${T} atomic_load_${T}( 541 | const atomic_${T}* a, 542 | int order 543 | ) { 544 | ${T} res; 545 | __atomic_load(&a->repr, &res, order); 546 | return res; 547 | } 548 | static inline void atomic_store_${T}( 549 | atomic_${T}* a, ${T} v, 550 | int order 551 | ) { 552 | __atomic_store(&a->repr, &v, order); 553 | } 554 | /* ... */ 555 | ) 556 | } 557 | 558 | async function atomicFor(T) { 559 | if (await has_std_atomics) { 560 | return atomicStdFor(T); 561 | } else { 562 | return atomicGccFor(T); 563 | } 564 | } 565 | 566 | #include 567 | #include 568 | typedef uint32_t u32; 569 | ${atomicFor(quote(u32))}; 570 | 571 | int main(void) { 572 | atomic_u32 i = atomic_new_u32(79); 573 | printf("i = %d\n", 574 | (int)atomic_load_u32(&i, Relaxed) 575 | ); 576 | atomic_store_u32(&i, 95, SeqCst); 577 | printf("i = %d\n", 578 | (int)atomic_load_u32(&i, AcqRel) 579 | ); 580 | } 581 | ``` 582 | ``` 583 | i = 79 584 | i = 95 585 | ``` 586 | 587 | ## Miscellaneous 588 | ```cpp 589 | #include 590 | 591 | class CppClass { 592 | public: 593 | void printStr(const char* s) { 594 | printf("%s\n", s); 595 | } 596 | }; 597 | 598 | jsclass JavaScriptClass { 599 | getIntegerStruct() { 600 | return quote( 601 | typedef struct { 602 | int v; 603 | } Integer; 604 | ) 605 | } 606 | } 607 | 608 | ${(new JavaScriptClass()).getIntegerStruct()}; 609 | 610 | int main(void) { 611 | const char* str = ${` 612 | multi-line strings in pure C! 613 | `}; 614 | (CppClass()).printStr(str); 615 | Integer i = (Integer)${{v: 10005}}; 616 | printf("JS objects -> C99 struct literals: %d\n", i.v); 617 | } 618 | ``` 619 | ``` 620 | 621 | multi-line strings in pure C! 622 | 623 | JS objects -> C99 struct literals: 10005 624 | ``` 625 | 626 | # Compilation 627 | Go 1.13 or newer is required. 628 | ``` 629 | git clone https://github.com/thooton/jc 630 | cd jc/src 631 | go build . 632 | ``` 633 | 634 | # Todo list 635 | - Module system: one can `inquire` another JC file, which consists of including all C in that file, as well as returning the value of the other file's JC_EXPORTS global variable. `const atomic = inquire "atomic.jh";` 636 | - Language server / integration with IDEs to provide proper syntax highlighting. 637 | 638 | # Contributing 639 | Any and all contributions are greatly appreciated. Just send a PR, issue, or feature request :) 640 | 641 | # License 642 | MIT 643 | -------------------------------------------------------------------------------- /src/ast.go: -------------------------------------------------------------------------------- 1 | // Code generated by goyacc -o ast.go -p ast ast.y. DO NOT EDIT. 2 | 3 | //line ast.y:1 4 | 5 | package main 6 | 7 | import __yyfmt__ "fmt" 8 | 9 | //line ast.y:3 10 | const ( 11 | AstBlockUnfin = 10 12 | AstBlock = 11 13 | AstQCircleUnfin = 12 14 | AstQCircle = 13 15 | AstQuoteUnfin = 14 16 | AstQuote = 15 17 | AstTicklitUnfin = 16 18 | AstTicklit = 17 19 | AstInterpolUnfin = 18 20 | AstInterpol = 19 21 | AstToplevel = 20 22 | AstMacro = 21 23 | AstLfnArgs = 22 24 | AstLfnRetType = 23 25 | AstLfnBody = 24 26 | AstEnd = 25 27 | ) 28 | 29 | /* this is a "union" between 30 | token and nodes. all of 31 | the actual token constants are enormous, 32 | so if token.kind < AstEnd, we know it is an 33 | []AstNode instead. */ 34 | type AstNode struct { 35 | token LexToken 36 | nodes []AstNode 37 | } 38 | 39 | //line ast.y:46 40 | type astSymType struct { 41 | yys int 42 | node AstNode 43 | } 44 | 45 | const TIdentifier = 57346 46 | const TConst = 57347 47 | const TStructUnionEnumClass = 57348 48 | const TFunctionForWhileJsclass = 57349 49 | const TJsOnly = 57350 50 | const TMinusGt = 57351 51 | const TEq = 57352 52 | const TLSquare = 57353 53 | const TRSquare = 57354 54 | const TOther = 57355 55 | const TLBrace = 57356 56 | const TRBrace = 57357 57 | const TQuote = 57358 58 | const TDollarLBrace = 57359 59 | const TTick = 57360 60 | const TLCircle = 57361 61 | const TRCircle = 57362 62 | const TSemi = 57363 63 | const TMacroStart = 57364 64 | const TMacroEnd = 57365 65 | 66 | var astToknames = [...]string{ 67 | "$end", 68 | "error", 69 | "$unk", 70 | "TIdentifier", 71 | "TConst", 72 | "TStructUnionEnumClass", 73 | "TFunctionForWhileJsclass", 74 | "TJsOnly", 75 | "TMinusGt", 76 | "TEq", 77 | "TLSquare", 78 | "TRSquare", 79 | "TOther", 80 | "TLBrace", 81 | "TRBrace", 82 | "TQuote", 83 | "TDollarLBrace", 84 | "TTick", 85 | "TLCircle", 86 | "TRCircle", 87 | "TSemi", 88 | "TMacroStart", 89 | "TMacroEnd", 90 | } 91 | 92 | var astStatenames = [...]string{} 93 | 94 | const astEofCode = 1 95 | const astErrCode = 2 96 | const astInitialStackSize = 16 97 | 98 | //line yacctab:1 99 | var astExca = [...]int8{ 100 | -1, 1, 101 | 1, -1, 102 | -2, 0, 103 | } 104 | 105 | const astPrivate = 57344 106 | 107 | const astLast = 192 108 | 109 | var astAct = [...]int8{ 110 | 9, 35, 50, 34, 78, 3, 4, 11, 33, 8, 111 | 45, 7, 27, 10, 49, 47, 68, 59, 46, 48, 112 | 1, 2, 25, 24, 42, 23, 57, 66, 77, 43, 113 | 37, 26, 52, 61, 70, 56, 65, 55, 64, 58, 114 | 67, 0, 0, 0, 42, 80, 0, 79, 0, 57, 115 | 37, 82, 0, 0, 0, 52, 0, 0, 56, 0, 116 | 55, 0, 58, 12, 13, 14, 15, 16, 17, 18, 117 | 19, 20, 21, 71, 72, 75, 30, 76, 73, 74, 118 | 22, 0, 69, 12, 13, 14, 15, 16, 17, 18, 119 | 19, 20, 21, 31, 0, 28, 30, 29, 5, 6, 120 | 22, 32, 12, 13, 14, 15, 16, 17, 18, 19, 121 | 20, 21, 31, 83, 28, 30, 29, 53, 54, 22, 122 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 123 | 38, 39, 40, 30, 41, 44, 81, 22, 12, 13, 124 | 14, 15, 16, 17, 18, 19, 20, 21, 31, 60, 125 | 28, 30, 29, 62, 63, 22, 12, 13, 14, 15, 126 | 16, 17, 18, 19, 20, 21, 31, 51, 28, 30, 127 | 29, 53, 54, 22, 12, 13, 14, 15, 16, 17, 128 | 18, 19, 20, 21, 38, 39, 40, 30, 41, 44, 129 | 36, 22, 130 | } 131 | 132 | var astPact = [...]int16{ 133 | 79, -1000, 79, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 134 | -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 135 | -1000, -1000, -1000, 170, -3, 152, 134, 59, -15, -1000, 136 | -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 137 | -1000, -1000, -1000, 116, -1000, -1000, -1000, -1000, 98, -1000, 138 | -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 139 | -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 140 | -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 141 | -1000, -1000, -1000, -1000, 142 | } 143 | 144 | var astPgo = [...]int8{ 145 | 0, 6, 31, 13, 3, 29, 1, 25, 11, 23, 146 | 9, 22, 0, 21, 20, 19, 18, 17, 2, 5, 147 | 16, 12, 7, 148 | } 149 | 150 | var astR1 = [...]int8{ 151 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 152 | 1, 1, 17, 17, 17, 17, 17, 17, 17, 2, 153 | 2, 3, 4, 4, 4, 4, 4, 4, 5, 5, 154 | 5, 6, 7, 7, 7, 8, 18, 18, 18, 18, 155 | 18, 18, 18, 15, 15, 16, 9, 9, 9, 10, 156 | 11, 11, 12, 20, 20, 20, 20, 20, 20, 20, 157 | 20, 21, 21, 22, 19, 19, 19, 19, 19, 19, 158 | 19, 19, 13, 13, 14, 159 | } 160 | 161 | var astR2 = [...]int8{ 162 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 163 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 164 | 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 165 | 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 166 | 1, 1, 1, 1, 2, 2, 1, 2, 2, 2, 167 | 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 168 | 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 169 | 1, 1, 1, 2, 1, 170 | } 171 | 172 | var astChk = [...]int16{ 173 | -1000, -14, -13, -19, -1, 19, 20, -8, -10, -12, 174 | -3, -22, 4, 5, 6, 7, 8, 9, 10, 11, 175 | 12, 13, 21, -7, -9, -11, -2, -21, 16, 18, 176 | 17, 14, 22, -19, -4, -6, 20, -1, 14, 15, 177 | 16, 18, -12, -5, 19, 13, -16, 18, -15, 17, 178 | -18, 15, -1, 19, 20, -8, -10, -12, -3, -17, 179 | 15, -1, 19, 20, -8, -10, -12, -3, -20, 23, 180 | -1, 14, 15, 19, 20, 16, 18, -12, 19, -4, 181 | -6, 20, -18, 15, 182 | } 183 | 184 | var astDef = [...]int8{ 185 | 0, -2, 74, 72, 64, 65, 66, 67, 68, 69, 186 | 70, 71, 1, 2, 3, 4, 5, 6, 7, 8, 187 | 9, 10, 11, 0, 0, 0, 0, 0, 0, 46, 188 | 50, 19, 61, 73, 33, 34, 35, 22, 23, 24, 189 | 25, 26, 27, 0, 28, 47, 48, 49, 0, 43, 190 | 51, 52, 36, 37, 38, 39, 40, 41, 42, 20, 191 | 21, 12, 13, 14, 15, 16, 17, 18, 62, 63, 192 | 53, 54, 55, 56, 57, 58, 59, 60, 32, 29, 193 | 30, 31, 44, 45, 194 | } 195 | 196 | var astTok1 = [...]int8{ 197 | 1, 198 | } 199 | 200 | var astTok2 = [...]int8{ 201 | 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 202 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 203 | 22, 23, 204 | } 205 | 206 | var astTok3 = [...]int8{ 207 | 0, 208 | } 209 | 210 | var astErrorMessages = [...]struct { 211 | state int 212 | token int 213 | msg string 214 | }{} 215 | 216 | //line yaccpar:1 217 | 218 | /* parser for yacc output */ 219 | 220 | var ( 221 | astDebug = 0 222 | astErrorVerbose = false 223 | ) 224 | 225 | type astLexer interface { 226 | Lex(lval *astSymType) int 227 | Error(s string) 228 | } 229 | 230 | type astParser interface { 231 | Parse(*ParserLexer) int 232 | Lookahead() int 233 | } 234 | 235 | type astParserImpl struct { 236 | lval astSymType 237 | stack [astInitialStackSize]astSymType 238 | char int 239 | } 240 | 241 | func (p *astParserImpl) Lookahead() int { 242 | return p.char 243 | } 244 | 245 | func astNewParser() astParser { 246 | return &astParserImpl{} 247 | } 248 | 249 | const astFlag = -1000 250 | 251 | func astTokname(c int) string { 252 | if c >= 1 && c-1 < len(astToknames) { 253 | if astToknames[c-1] != "" { 254 | return astToknames[c-1] 255 | } 256 | } 257 | return __yyfmt__.Sprintf("tok-%v", c) 258 | } 259 | 260 | func astStatname(s int) string { 261 | if s >= 0 && s < len(astStatenames) { 262 | if astStatenames[s] != "" { 263 | return astStatenames[s] 264 | } 265 | } 266 | return __yyfmt__.Sprintf("state-%v", s) 267 | } 268 | 269 | func astErrorMessage(state, lookAhead int) string { 270 | const TOKSTART = 4 271 | 272 | if !astErrorVerbose { 273 | return "syntax error" 274 | } 275 | 276 | for _, e := range astErrorMessages { 277 | if e.state == state && e.token == lookAhead { 278 | return "syntax error: " + e.msg 279 | } 280 | } 281 | 282 | res := "syntax error: unexpected " + astTokname(lookAhead) 283 | 284 | // To match Bison, suggest at most four expected tokens. 285 | expected := make([]int, 0, 4) 286 | 287 | // Look for shiftable tokens. 288 | base := int(astPact[state]) 289 | for tok := TOKSTART; tok-1 < len(astToknames); tok++ { 290 | if n := base + tok; n >= 0 && n < astLast && int(astChk[int(astAct[n])]) == tok { 291 | if len(expected) == cap(expected) { 292 | return res 293 | } 294 | expected = append(expected, tok) 295 | } 296 | } 297 | 298 | if astDef[state] == -2 { 299 | i := 0 300 | for astExca[i] != -1 || int(astExca[i+1]) != state { 301 | i += 2 302 | } 303 | 304 | // Look for tokens that we accept or reduce. 305 | for i += 2; astExca[i] >= 0; i += 2 { 306 | tok := int(astExca[i]) 307 | if tok < TOKSTART || astExca[i+1] == 0 { 308 | continue 309 | } 310 | if len(expected) == cap(expected) { 311 | return res 312 | } 313 | expected = append(expected, tok) 314 | } 315 | 316 | // If the default action is to accept or reduce, give up. 317 | if astExca[i+1] != 0 { 318 | return res 319 | } 320 | } 321 | 322 | for i, tok := range expected { 323 | if i == 0 { 324 | res += ", expecting " 325 | } else { 326 | res += " or " 327 | } 328 | res += astTokname(tok) 329 | } 330 | return res 331 | } 332 | 333 | func astlex1(lex *ParserLexer, lval *astSymType) (char, token int) { 334 | token = 0 335 | char = lex.Lex(lval) 336 | if char <= 0 { 337 | token = int(astTok1[0]) 338 | goto out 339 | } 340 | if char < len(astTok1) { 341 | token = int(astTok1[char]) 342 | goto out 343 | } 344 | if char >= astPrivate { 345 | if char < astPrivate+len(astTok2) { 346 | token = int(astTok2[char-astPrivate]) 347 | goto out 348 | } 349 | } 350 | for i := 0; i < len(astTok3); i += 2 { 351 | token = int(astTok3[i+0]) 352 | if token == char { 353 | token = int(astTok3[i+1]) 354 | goto out 355 | } 356 | } 357 | 358 | out: 359 | if token == 0 { 360 | token = int(astTok2[1]) /* unknown char */ 361 | } 362 | if astDebug >= 3 { 363 | __yyfmt__.Printf("lex %s(%d)\n", astTokname(token), uint(char)) 364 | } 365 | return char, token 366 | } 367 | 368 | func astParse(astlex *ParserLexer) int { 369 | return astNewParser().Parse(astlex) 370 | } 371 | 372 | func (astrcvr *astParserImpl) Parse(astlex *ParserLexer) int { 373 | var astn int 374 | var astVAL astSymType 375 | var astDollar []astSymType 376 | _ = astDollar // silence set and not used 377 | astS := astrcvr.stack[:] 378 | 379 | Nerrs := 0 /* number of errors */ 380 | Errflag := 0 /* error recovery flag */ 381 | aststate := 0 382 | astrcvr.char = -1 383 | asttoken := -1 // astrcvr.char translated into internal numbering 384 | defer func() { 385 | // Make sure we report no lookahead when not parsing. 386 | aststate = -1 387 | astrcvr.char = -1 388 | asttoken = -1 389 | }() 390 | astp := -1 391 | goto aststack 392 | 393 | ret0: 394 | return 0 395 | 396 | ret1: 397 | return 1 398 | 399 | aststack: 400 | /* put a state and value onto the stack */ 401 | if astDebug >= 4 { 402 | __yyfmt__.Printf("char %v in %v\n", astTokname(asttoken), astStatname(aststate)) 403 | } 404 | 405 | astp++ 406 | if astp >= len(astS) { 407 | nyys := make([]astSymType, len(astS)*2) 408 | copy(nyys, astS) 409 | astS = nyys 410 | } 411 | astS[astp] = astVAL 412 | astS[astp].yys = aststate 413 | 414 | astnewstate: 415 | astn = int(astPact[aststate]) 416 | if astn <= astFlag { 417 | goto astdefault /* simple state */ 418 | } 419 | if astrcvr.char < 0 { 420 | astrcvr.char, asttoken = astlex1(astlex, &astrcvr.lval) 421 | } 422 | astn += asttoken 423 | if astn < 0 || astn >= astLast { 424 | goto astdefault 425 | } 426 | astn = int(astAct[astn]) 427 | if int(astChk[astn]) == asttoken { /* valid shift */ 428 | astrcvr.char = -1 429 | asttoken = -1 430 | astVAL = astrcvr.lval 431 | aststate = astn 432 | if Errflag > 0 { 433 | Errflag-- 434 | } 435 | goto aststack 436 | } 437 | 438 | astdefault: 439 | /* default state action */ 440 | astn = int(astDef[aststate]) 441 | if astn == -2 { 442 | if astrcvr.char < 0 { 443 | astrcvr.char, asttoken = astlex1(astlex, &astrcvr.lval) 444 | } 445 | 446 | /* look through exception table */ 447 | xi := 0 448 | for { 449 | if astExca[xi+0] == -1 && int(astExca[xi+1]) == aststate { 450 | break 451 | } 452 | xi += 2 453 | } 454 | for xi += 2; ; xi += 2 { 455 | astn = int(astExca[xi+0]) 456 | if astn < 0 || astn == asttoken { 457 | break 458 | } 459 | } 460 | astn = int(astExca[xi+1]) 461 | if astn < 0 { 462 | goto ret0 463 | } 464 | } 465 | if astn == 0 { 466 | /* error ... attempt to resume parsing */ 467 | switch Errflag { 468 | case 0: /* brand new error */ 469 | astlex.Error(astErrorMessage(aststate, asttoken)) 470 | Nerrs++ 471 | if astDebug >= 1 { 472 | __yyfmt__.Printf("%s", astStatname(aststate)) 473 | __yyfmt__.Printf(" saw %s\n", astTokname(asttoken)) 474 | } 475 | fallthrough 476 | 477 | case 1, 2: /* incompletely recovered error ... try again */ 478 | Errflag = 3 479 | 480 | /* find a state where "error" is a legal shift action */ 481 | for astp >= 0 { 482 | astn = int(astPact[astS[astp].yys]) + astErrCode 483 | if astn >= 0 && astn < astLast { 484 | aststate = int(astAct[astn]) /* simulate a shift of "error" */ 485 | if int(astChk[aststate]) == astErrCode { 486 | goto aststack 487 | } 488 | } 489 | 490 | /* the current p has no shift on "error", pop stack */ 491 | if astDebug >= 2 { 492 | __yyfmt__.Printf("error recovery pops state %d\n", astS[astp].yys) 493 | } 494 | astp-- 495 | } 496 | /* there is no state on the stack with an error shift ... abort */ 497 | goto ret1 498 | 499 | case 3: /* no shift yet; clobber input char */ 500 | if astDebug >= 2 { 501 | __yyfmt__.Printf("error recovery discards %s\n", astTokname(asttoken)) 502 | } 503 | if asttoken == astEofCode { 504 | goto ret1 505 | } 506 | astrcvr.char = -1 507 | asttoken = -1 508 | goto astnewstate /* try again in the same state */ 509 | } 510 | } 511 | 512 | /* reduction by production astn */ 513 | if astDebug >= 2 { 514 | __yyfmt__.Printf("reduce %v in:\n\t%v\n", astn, astStatname(aststate)) 515 | } 516 | 517 | astnt := astn 518 | astpt := astp 519 | _ = astpt // guard against "declared and not used" 520 | 521 | astp -= int(astR2[astn]) 522 | // astp is now the index of $0. Perform the default action. Iff the 523 | // reduced production is ε, $1 is possibly out of range. 524 | if astp+1 >= len(astS) { 525 | nyys := make([]astSymType, len(astS)*2) 526 | copy(nyys, astS) 527 | astS = nyys 528 | } 529 | astVAL = astS[astp+1] 530 | 531 | /* consult goto table to find next state */ 532 | astn = int(astR1[astn]) 533 | astg := int(astPgo[astn]) 534 | astj := astg + astS[astp].yys + 1 535 | 536 | if astj >= astLast { 537 | aststate = int(astAct[astg]) 538 | } else { 539 | aststate = int(astAct[astj]) 540 | if int(astChk[aststate]) != -astn { 541 | aststate = int(astAct[astg]) 542 | } 543 | } 544 | // dummy call; replaced with literal code 545 | switch astnt { 546 | 547 | case 19: 548 | astDollar = astS[astpt-1 : astpt+1] 549 | //line ast.y:99 550 | { 551 | astlex.beginBlock() 552 | astVAL.node = AstNode{LexToken{0, 0, AstBlockUnfin}, []AstNode{astDollar[1].node}} 553 | } 554 | case 20: 555 | astDollar = astS[astpt-2 : astpt+1] 556 | //line ast.y:100 557 | { 558 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 559 | astVAL.node = astDollar[1].node 560 | } 561 | case 21: 562 | astDollar = astS[astpt-2 : astpt+1] 563 | //line ast.y:103 564 | { 565 | astlex.endBlock() 566 | astVAL.node = AstNode{LexToken{0, 0, AstBlock}, append(astDollar[1].node.nodes, astDollar[2].node)} 567 | } 568 | case 28: 569 | astDollar = astS[astpt-1 : astpt+1] 570 | //line ast.y:115 571 | { 572 | astVAL.node = AstNode{LexToken{0, 0, AstQCircleUnfin}, []AstNode{astDollar[1].node}} 573 | } 574 | case 29: 575 | astDollar = astS[astpt-2 : astpt+1] 576 | //line ast.y:116 577 | { 578 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 579 | astVAL.node = astDollar[1].node 580 | } 581 | case 30: 582 | astDollar = astS[astpt-2 : astpt+1] 583 | //line ast.y:117 584 | { 585 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node.nodes...) 586 | astVAL.node = astDollar[1].node 587 | } 588 | case 32: 589 | astDollar = astS[astpt-2 : astpt+1] 590 | //line ast.y:124 591 | { 592 | astVAL.node = AstNode{LexToken{0, 0, AstQuoteUnfin}, []AstNode{astDollar[1].node, astDollar[2].node}} 593 | } 594 | case 33: 595 | astDollar = astS[astpt-2 : astpt+1] 596 | //line ast.y:126 597 | { 598 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 599 | astVAL.node = astDollar[1].node 600 | } 601 | case 34: 602 | astDollar = astS[astpt-2 : astpt+1] 603 | //line ast.y:128 604 | { 605 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node.nodes...) 606 | astVAL.node = astDollar[1].node 607 | } 608 | case 35: 609 | astDollar = astS[astpt-2 : astpt+1] 610 | //line ast.y:131 611 | { 612 | astVAL.node = AstNode{LexToken{0, 0, AstQuote}, append(astDollar[1].node.nodes, astDollar[2].node)} 613 | } 614 | case 43: 615 | astDollar = astS[astpt-1 : astpt+1] 616 | //line ast.y:145 617 | { 618 | astlex.beginBlock() 619 | astlex.outTicks() 620 | astVAL.node = AstNode{LexToken{0, 0, AstInterpolUnfin}, []AstNode{astDollar[1].node}} 621 | } 622 | case 44: 623 | astDollar = astS[astpt-2 : astpt+1] 624 | //line ast.y:146 625 | { 626 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 627 | astVAL.node = astDollar[1].node 628 | } 629 | case 45: 630 | astDollar = astS[astpt-2 : astpt+1] 631 | //line ast.y:149 632 | { 633 | astlex.endBlock() 634 | astlex.inTicks() 635 | astVAL.node = AstNode{LexToken{0, 0, AstInterpol}, append(astDollar[1].node.nodes, astDollar[2].node)} 636 | } 637 | case 46: 638 | astDollar = astS[astpt-1 : astpt+1] 639 | //line ast.y:152 640 | { 641 | astlex.inTicks() 642 | astVAL.node = AstNode{LexToken{0, 0, AstTicklitUnfin}, []AstNode{astDollar[1].node}} 643 | } 644 | case 47: 645 | astDollar = astS[astpt-2 : astpt+1] 646 | //line ast.y:153 647 | { 648 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 649 | astVAL.node = astDollar[1].node 650 | } 651 | case 48: 652 | astDollar = astS[astpt-2 : astpt+1] 653 | //line ast.y:154 654 | { 655 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 656 | astVAL.node = astDollar[1].node 657 | } 658 | case 49: 659 | astDollar = astS[astpt-2 : astpt+1] 660 | //line ast.y:158 661 | { 662 | astlex.outTicks() 663 | astVAL.node = AstNode{LexToken{0, 0, AstTicklit}, append(astDollar[1].node.nodes, astDollar[2].node)} 664 | } 665 | case 50: 666 | astDollar = astS[astpt-1 : astpt+1] 667 | //line ast.y:162 668 | { 669 | astlex.beginBlock() 670 | astVAL.node = AstNode{LexToken{0, 0, AstInterpolUnfin}, []AstNode{astDollar[1].node}} 671 | } 672 | case 51: 673 | astDollar = astS[astpt-2 : astpt+1] 674 | //line ast.y:163 675 | { 676 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 677 | astVAL.node = astDollar[1].node 678 | } 679 | case 52: 680 | astDollar = astS[astpt-2 : astpt+1] 681 | //line ast.y:166 682 | { 683 | astlex.endBlock() 684 | astVAL.node = AstNode{LexToken{0, 0, AstInterpol}, append(astDollar[1].node.nodes, astDollar[2].node)} 685 | } 686 | case 61: 687 | astDollar = astS[astpt-1 : astpt+1] 688 | //line ast.y:180 689 | { 690 | astVAL.node = AstNode{LexToken{0, 0, AstMacro}, []AstNode{astDollar[1].node}} 691 | } 692 | case 62: 693 | astDollar = astS[astpt-2 : astpt+1] 694 | //line ast.y:181 695 | { 696 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 697 | astVAL.node = astDollar[1].node 698 | } 699 | case 72: 700 | astDollar = astS[astpt-1 : astpt+1] 701 | //line ast.y:197 702 | { 703 | astVAL.node = AstNode{LexToken{0, 0, AstToplevel}, []AstNode{astDollar[1].node}} 704 | } 705 | case 73: 706 | astDollar = astS[astpt-2 : astpt+1] 707 | //line ast.y:198 708 | { 709 | astDollar[1].node.nodes = append(astDollar[1].node.nodes, astDollar[2].node) 710 | astVAL.node = astDollar[1].node 711 | } 712 | case 74: 713 | astDollar = astS[astpt-1 : astpt+1] 714 | //line ast.y:202 715 | { 716 | astlex.receiveResult(astDollar[1].node) 717 | return 0 718 | } 719 | } 720 | goto aststack /* stack new state and value */ 721 | } 722 | -------------------------------------------------------------------------------- /src/lexer.go: -------------------------------------------------------------------------------- 1 | // Code generated by re2c 3.0 on Wed Jan 11 15:48:43 2023, DO NOT EDIT. 2 | package main 3 | 4 | import "bytes" 5 | 6 | const ( 7 | TEof = 0 8 | TInvalid = 1 9 | TPLBefore = 2 10 | TPLNewline = 3 11 | TPLValue = 4 12 | TPLInc = 5 13 | TPLDec = 6 14 | TPLPound = 7 15 | TPLSlash = 8 16 | TPLAfter = 9 17 | ) 18 | 19 | type LexToken struct { 20 | begin, end, kind uint32 21 | } 22 | 23 | func lexRawQuoteLit(input []byte, cursor uint) uint { 24 | inp := input[cursor:] 25 | dseq_end := bytes.IndexByte(inp, '(') 26 | if dseq_end == -1 { 27 | return uint(len(input)) 28 | } 29 | eseq := make([]byte, 0, 2+dseq_end) 30 | eseq = append(eseq, ')') 31 | eseq = append(eseq, inp[:dseq_end]...) 32 | eseq = append(eseq, '"') 33 | eseq_loc := bytes.Index(inp[dseq_end+1:], eseq) 34 | if eseq_loc == -1 { 35 | return uint(len(input)) 36 | } 37 | return cursor+uint(dseq_end+1+eseq_loc+len(eseq)) 38 | } 39 | 40 | func lexNextToken(input []byte, cursor uint) LexToken { 41 | var began uint = cursor 42 | var marker uint = 0 43 | peek_next := func(str []byte, i uint) byte { 44 | if i < uint(len(str)) { 45 | return str[i] 46 | } else { 47 | return 0 48 | } 49 | } 50 | lex_start: 51 | 52 | { 53 | var yych byte 54 | yyaccept := 0 55 | yych = peek_next(input, cursor) 56 | switch (yych) { 57 | case 0x00: 58 | goto yy1 59 | case 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t': 60 | fallthrough 61 | case '\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ': 62 | fallthrough 63 | case 0x85: 64 | goto yy2 65 | case '\n': 66 | goto yy4 67 | case '!': 68 | goto yy5 69 | case '"': 70 | goto yy7 71 | case '#': 72 | goto yy9 73 | case '$': 74 | goto yy10 75 | case '%': 76 | fallthrough 77 | case '^': 78 | goto yy11 79 | case '&': 80 | goto yy12 81 | case '\'': 82 | goto yy13 83 | case '(': 84 | goto yy15 85 | case ')': 86 | goto yy16 87 | case '*': 88 | goto yy17 89 | case '+': 90 | goto yy18 91 | case ',': 92 | fallthrough 93 | case '~': 94 | goto yy19 95 | case '-': 96 | goto yy20 97 | case '.': 98 | goto yy21 99 | case '/': 100 | goto yy22 101 | case '0': 102 | goto yy24 103 | case '1','2','3','4','5','6','7','8','9': 104 | goto yy25 105 | case ':': 106 | goto yy27 107 | case ';': 108 | goto yy28 109 | case '<': 110 | goto yy29 111 | case '=': 112 | goto yy30 113 | case '>': 114 | goto yy32 115 | case '?': 116 | goto yy33 117 | case '@': 118 | fallthrough 119 | case 0x7F: 120 | goto yy34 121 | case 'L': 122 | fallthrough 123 | case 'U': 124 | goto yy38 125 | case 'R': 126 | goto yy39 127 | case '[': 128 | goto yy40 129 | case '\\': 130 | goto yy41 131 | case ']': 132 | goto yy42 133 | case '`': 134 | goto yy43 135 | case 'a': 136 | goto yy44 137 | case 'c': 138 | goto yy45 139 | case 'e': 140 | goto yy46 141 | case 'f': 142 | goto yy47 143 | case 'j': 144 | goto yy48 145 | case 'l': 146 | goto yy49 147 | case 'q': 148 | goto yy50 149 | case 's': 150 | goto yy51 151 | case 'u': 152 | goto yy52 153 | case 'v': 154 | goto yy53 155 | case 'w': 156 | goto yy54 157 | case '{': 158 | goto yy55 159 | case '|': 160 | goto yy56 161 | case '}': 162 | goto yy57 163 | default: 164 | goto yy35 165 | } 166 | yy1: 167 | cursor++ 168 | { 169 | return LexToken{0, 0, TEof} 170 | } 171 | yy2: 172 | yyaccept = 0 173 | cursor++ 174 | marker = cursor 175 | yych = peek_next(input, cursor) 176 | switch (yych) { 177 | case 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t': 178 | fallthrough 179 | case '\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ': 180 | fallthrough 181 | case 0x85: 182 | goto yy2 183 | case '/': 184 | goto yy58 185 | case '\\': 186 | goto yy60 187 | default: 188 | goto yy3 189 | } 190 | yy3: 191 | { 192 | began = cursor 193 | goto lex_start 194 | } 195 | yy4: 196 | cursor++ 197 | { 198 | return LexToken{uint32(began), uint32(cursor), TPLNewline} 199 | } 200 | yy5: 201 | cursor++ 202 | yych = peek_next(input, cursor) 203 | switch (yych) { 204 | case '=': 205 | goto yy61 206 | default: 207 | goto yy6 208 | } 209 | yy6: 210 | { 211 | return LexToken{uint32(began), uint32(cursor), TOther} 212 | } 213 | yy7: 214 | yyaccept = 1 215 | cursor++ 216 | marker = cursor 217 | yych = peek_next(input, cursor) 218 | switch (yych) { 219 | case 0x00: 220 | fallthrough 221 | case '\n': 222 | goto yy8 223 | default: 224 | goto yy64 225 | } 226 | yy8: 227 | { 228 | return LexToken{0, 0, TInvalid} 229 | } 230 | yy9: 231 | cursor++ 232 | { 233 | return LexToken{uint32(began), uint32(cursor), TPLPound} 234 | } 235 | yy10: 236 | cursor++ 237 | yych = peek_next(input, cursor) 238 | switch (yych) { 239 | case '{': 240 | goto yy67 241 | default: 242 | goto yy8 243 | } 244 | yy11: 245 | cursor++ 246 | yych = peek_next(input, cursor) 247 | switch (yych) { 248 | case '=': 249 | goto yy68 250 | default: 251 | goto yy6 252 | } 253 | yy12: 254 | cursor++ 255 | yych = peek_next(input, cursor) 256 | switch (yych) { 257 | case '&': 258 | fallthrough 259 | case '=': 260 | goto yy68 261 | default: 262 | goto yy6 263 | } 264 | yy13: 265 | yyaccept = 2 266 | cursor++ 267 | marker = cursor 268 | yych = peek_next(input, cursor) 269 | switch (yych) { 270 | case 0x00: 271 | fallthrough 272 | case '\n': 273 | goto yy14 274 | case '\'': 275 | goto yy25 276 | case '.': 277 | goto yy71 278 | case '0','1','2','3','4','5','6','7','8','9': 279 | goto yy13 280 | case 'E': 281 | fallthrough 282 | case 'e': 283 | goto yy72 284 | case 'L': 285 | fallthrough 286 | case 'U': 287 | fallthrough 288 | case 'Z': 289 | fallthrough 290 | case 'l': 291 | fallthrough 292 | case 'u': 293 | fallthrough 294 | case 'z': 295 | goto yy73 296 | case '\\': 297 | goto yy74 298 | case 'n': 299 | goto yy75 300 | default: 301 | goto yy69 302 | } 303 | yy14: 304 | { 305 | return LexToken{uint32(began), uint32(cursor), TPLValue} 306 | } 307 | yy15: 308 | cursor++ 309 | { 310 | return LexToken{uint32(began), uint32(cursor), TLCircle} 311 | } 312 | yy16: 313 | cursor++ 314 | { 315 | return LexToken{uint32(began), uint32(cursor), TRCircle} 316 | } 317 | yy17: 318 | cursor++ 319 | yych = peek_next(input, cursor) 320 | switch (yych) { 321 | case '*': 322 | goto yy61 323 | case '=': 324 | goto yy68 325 | default: 326 | goto yy6 327 | } 328 | yy18: 329 | cursor++ 330 | yych = peek_next(input, cursor) 331 | switch (yych) { 332 | case '+': 333 | goto yy76 334 | case '=': 335 | goto yy68 336 | default: 337 | goto yy6 338 | } 339 | yy19: 340 | cursor++ 341 | goto yy6 342 | yy20: 343 | cursor++ 344 | yych = peek_next(input, cursor) 345 | switch (yych) { 346 | case '-': 347 | goto yy77 348 | case '=': 349 | goto yy68 350 | case '>': 351 | goto yy78 352 | default: 353 | goto yy6 354 | } 355 | yy21: 356 | yyaccept = 3 357 | cursor++ 358 | marker = cursor 359 | yych = peek_next(input, cursor) 360 | switch (yych) { 361 | case '\'': 362 | fallthrough 363 | case '0','1','2','3','4','5','6','7','8','9': 364 | goto yy79 365 | case '.': 366 | goto yy80 367 | default: 368 | goto yy6 369 | } 370 | yy22: 371 | cursor++ 372 | yych = peek_next(input, cursor) 373 | switch (yych) { 374 | case '*': 375 | goto yy81 376 | case '/': 377 | goto yy82 378 | case '=': 379 | goto yy68 380 | default: 381 | goto yy23 382 | } 383 | yy23: 384 | { 385 | return LexToken{uint32(began), uint32(cursor), TPLSlash} 386 | } 387 | yy24: 388 | yyaccept = 2 389 | cursor++ 390 | marker = cursor 391 | yych = peek_next(input, cursor) 392 | switch (yych) { 393 | case 'B': 394 | fallthrough 395 | case 'b': 396 | goto yy83 397 | case 'O': 398 | fallthrough 399 | case 'o': 400 | goto yy86 401 | case 'X': 402 | fallthrough 403 | case 'x': 404 | goto yy87 405 | default: 406 | goto yy26 407 | } 408 | yy25: 409 | yyaccept = 2 410 | cursor++ 411 | marker = cursor 412 | yych = peek_next(input, cursor) 413 | yy26: 414 | switch (yych) { 415 | case '\'': 416 | fallthrough 417 | case '0','1','2','3','4','5','6','7','8','9': 418 | goto yy25 419 | case '.': 420 | goto yy79 421 | case 'E': 422 | fallthrough 423 | case 'e': 424 | goto yy84 425 | case 'L': 426 | fallthrough 427 | case 'U': 428 | fallthrough 429 | case 'Z': 430 | fallthrough 431 | case 'l': 432 | fallthrough 433 | case 'u': 434 | fallthrough 435 | case 'z': 436 | goto yy85 437 | case 'n': 438 | goto yy65 439 | default: 440 | goto yy14 441 | } 442 | yy27: 443 | cursor++ 444 | yych = peek_next(input, cursor) 445 | switch (yych) { 446 | case ':': 447 | goto yy68 448 | default: 449 | goto yy6 450 | } 451 | yy28: 452 | cursor++ 453 | { 454 | return LexToken{uint32(began), uint32(cursor), TSemi} 455 | } 456 | yy29: 457 | cursor++ 458 | yych = peek_next(input, cursor) 459 | switch (yych) { 460 | case '<': 461 | goto yy61 462 | case '=': 463 | goto yy88 464 | default: 465 | goto yy6 466 | } 467 | yy30: 468 | cursor++ 469 | yych = peek_next(input, cursor) 470 | switch (yych) { 471 | case '=': 472 | goto yy61 473 | default: 474 | goto yy31 475 | } 476 | yy31: 477 | { 478 | return LexToken{uint32(began), uint32(cursor), TEq} 479 | } 480 | yy32: 481 | cursor++ 482 | yych = peek_next(input, cursor) 483 | switch (yych) { 484 | case '=': 485 | goto yy61 486 | case '>': 487 | goto yy89 488 | default: 489 | goto yy6 490 | } 491 | yy33: 492 | cursor++ 493 | yych = peek_next(input, cursor) 494 | switch (yych) { 495 | case '?': 496 | goto yy61 497 | default: 498 | goto yy6 499 | } 500 | yy34: 501 | cursor++ 502 | goto yy8 503 | yy35: 504 | cursor++ 505 | yych = peek_next(input, cursor) 506 | yy36: 507 | switch (yych) { 508 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 509 | fallthrough 510 | case ':',';','<','=','>','?','@': 511 | fallthrough 512 | case '[','\\',']','^': 513 | fallthrough 514 | case '`': 515 | fallthrough 516 | case '{','|','}','~',0x7F: 517 | fallthrough 518 | case 0x85: 519 | goto yy37 520 | default: 521 | goto yy35 522 | } 523 | yy37: 524 | { 525 | return LexToken{uint32(began), uint32(cursor), TIdentifier} 526 | } 527 | yy38: 528 | yyaccept = 4 529 | cursor++ 530 | marker = cursor 531 | yych = peek_next(input, cursor) 532 | switch (yych) { 533 | case '"': 534 | goto yy63 535 | case '\'': 536 | goto yy69 537 | case 'R': 538 | goto yy39 539 | default: 540 | goto yy36 541 | } 542 | yy39: 543 | cursor++ 544 | yych = peek_next(input, cursor) 545 | switch (yych) { 546 | case '"': 547 | goto yy90 548 | default: 549 | goto yy36 550 | } 551 | yy40: 552 | cursor++ 553 | { 554 | return LexToken{uint32(began), uint32(cursor), TLSquare} 555 | } 556 | yy41: 557 | yyaccept = 1 558 | cursor++ 559 | marker = cursor 560 | yych = peek_next(input, cursor) 561 | switch (yych) { 562 | case '\n': 563 | goto yy2 564 | case '\r': 565 | goto yy91 566 | default: 567 | goto yy8 568 | } 569 | yy42: 570 | cursor++ 571 | { 572 | return LexToken{uint32(began), uint32(cursor), TRSquare} 573 | } 574 | yy43: 575 | cursor++ 576 | { 577 | return LexToken{uint32(began), uint32(cursor), TTick} 578 | } 579 | yy44: 580 | cursor++ 581 | yych = peek_next(input, cursor) 582 | switch (yych) { 583 | case 's': 584 | goto yy92 585 | default: 586 | goto yy36 587 | } 588 | yy45: 589 | cursor++ 590 | yych = peek_next(input, cursor) 591 | switch (yych) { 592 | case 'l': 593 | goto yy93 594 | case 'o': 595 | goto yy94 596 | default: 597 | goto yy36 598 | } 599 | yy46: 600 | cursor++ 601 | yych = peek_next(input, cursor) 602 | switch (yych) { 603 | case 'n': 604 | goto yy95 605 | default: 606 | goto yy36 607 | } 608 | yy47: 609 | cursor++ 610 | yych = peek_next(input, cursor) 611 | switch (yych) { 612 | case 'o': 613 | goto yy96 614 | case 'u': 615 | goto yy97 616 | default: 617 | goto yy36 618 | } 619 | yy48: 620 | cursor++ 621 | yych = peek_next(input, cursor) 622 | switch (yych) { 623 | case 's': 624 | goto yy98 625 | default: 626 | goto yy36 627 | } 628 | yy49: 629 | cursor++ 630 | yych = peek_next(input, cursor) 631 | switch (yych) { 632 | case 'e': 633 | goto yy99 634 | default: 635 | goto yy36 636 | } 637 | yy50: 638 | cursor++ 639 | yych = peek_next(input, cursor) 640 | switch (yych) { 641 | case 'u': 642 | goto yy100 643 | default: 644 | goto yy36 645 | } 646 | yy51: 647 | cursor++ 648 | yych = peek_next(input, cursor) 649 | switch (yych) { 650 | case 't': 651 | goto yy101 652 | default: 653 | goto yy36 654 | } 655 | yy52: 656 | yyaccept = 4 657 | cursor++ 658 | marker = cursor 659 | yych = peek_next(input, cursor) 660 | switch (yych) { 661 | case '"': 662 | goto yy63 663 | case '\'': 664 | goto yy69 665 | case '8': 666 | goto yy38 667 | case 'R': 668 | goto yy39 669 | case 'n': 670 | goto yy102 671 | default: 672 | goto yy36 673 | } 674 | yy53: 675 | cursor++ 676 | yych = peek_next(input, cursor) 677 | switch (yych) { 678 | case 'a': 679 | goto yy103 680 | default: 681 | goto yy36 682 | } 683 | yy54: 684 | cursor++ 685 | yych = peek_next(input, cursor) 686 | switch (yych) { 687 | case 'h': 688 | goto yy104 689 | default: 690 | goto yy36 691 | } 692 | yy55: 693 | cursor++ 694 | { 695 | return LexToken{uint32(began), uint32(cursor), TLBrace} 696 | } 697 | yy56: 698 | cursor++ 699 | yych = peek_next(input, cursor) 700 | switch (yych) { 701 | case '=': 702 | fallthrough 703 | case '|': 704 | goto yy68 705 | default: 706 | goto yy6 707 | } 708 | yy57: 709 | cursor++ 710 | { 711 | return LexToken{uint32(began), uint32(cursor), TRBrace} 712 | } 713 | yy58: 714 | cursor++ 715 | yych = peek_next(input, cursor) 716 | switch (yych) { 717 | case '/': 718 | goto yy82 719 | default: 720 | goto yy59 721 | } 722 | yy59: 723 | cursor = marker 724 | switch (yyaccept) { 725 | case 0: 726 | goto yy3 727 | case 1: 728 | goto yy8 729 | case 2: 730 | goto yy14 731 | case 3: 732 | goto yy6 733 | default: 734 | goto yy37 735 | } 736 | yy60: 737 | cursor++ 738 | yych = peek_next(input, cursor) 739 | switch (yych) { 740 | case '\n': 741 | goto yy2 742 | case '\r': 743 | goto yy91 744 | default: 745 | goto yy59 746 | } 747 | yy61: 748 | cursor++ 749 | yych = peek_next(input, cursor) 750 | switch (yych) { 751 | case '=': 752 | goto yy68 753 | default: 754 | goto yy62 755 | } 756 | yy62: 757 | { 758 | return LexToken{uint32(began), uint32(cursor), TOther} 759 | } 760 | yy63: 761 | cursor++ 762 | yych = peek_next(input, cursor) 763 | yy64: 764 | switch (yych) { 765 | case 0x00: 766 | fallthrough 767 | case '\n': 768 | goto yy59 769 | case '"': 770 | goto yy65 771 | case '\\': 772 | goto yy66 773 | default: 774 | goto yy63 775 | } 776 | yy65: 777 | cursor++ 778 | goto yy14 779 | yy66: 780 | cursor++ 781 | yych = peek_next(input, cursor) 782 | if (yych <= 0x00) { 783 | goto yy59 784 | } 785 | goto yy63 786 | yy67: 787 | cursor++ 788 | { 789 | return LexToken{uint32(began), uint32(cursor), TDollarLBrace} 790 | } 791 | yy68: 792 | cursor++ 793 | goto yy62 794 | yy69: 795 | cursor++ 796 | yych = peek_next(input, cursor) 797 | yy70: 798 | switch (yych) { 799 | case 0x00: 800 | fallthrough 801 | case '\n': 802 | goto yy59 803 | case '\'': 804 | goto yy65 805 | case '\\': 806 | goto yy74 807 | default: 808 | goto yy69 809 | } 810 | yy71: 811 | yyaccept = 2 812 | cursor++ 813 | marker = cursor 814 | yych = peek_next(input, cursor) 815 | switch (yych) { 816 | case 0x00: 817 | fallthrough 818 | case '\n': 819 | goto yy14 820 | case '\'': 821 | goto yy79 822 | case '0','1','2','3','4','5','6','7','8','9': 823 | goto yy71 824 | case 'B': 825 | goto yy105 826 | case 'E': 827 | fallthrough 828 | case 'e': 829 | goto yy72 830 | case 'F': 831 | fallthrough 832 | case 'f': 833 | goto yy106 834 | case 'L': 835 | fallthrough 836 | case 'l': 837 | goto yy75 838 | case '\\': 839 | goto yy74 840 | case 'b': 841 | goto yy107 842 | default: 843 | goto yy69 844 | } 845 | yy72: 846 | cursor++ 847 | yych = peek_next(input, cursor) 848 | switch (yych) { 849 | case '\'': 850 | goto yy108 851 | case '+': 852 | fallthrough 853 | case '-': 854 | goto yy109 855 | case '0','1','2','3','4','5','6','7','8','9': 856 | goto yy110 857 | default: 858 | goto yy70 859 | } 860 | yy73: 861 | yyaccept = 2 862 | cursor++ 863 | marker = cursor 864 | yych = peek_next(input, cursor) 865 | switch (yych) { 866 | case 0x00: 867 | fallthrough 868 | case '\n': 869 | goto yy14 870 | case '\'': 871 | goto yy65 872 | case 'L': 873 | fallthrough 874 | case 'U': 875 | fallthrough 876 | case 'Z': 877 | fallthrough 878 | case 'l': 879 | fallthrough 880 | case 'u': 881 | fallthrough 882 | case 'z': 883 | goto yy73 884 | case '\\': 885 | goto yy74 886 | default: 887 | goto yy69 888 | } 889 | yy74: 890 | cursor++ 891 | yych = peek_next(input, cursor) 892 | if (yych <= 0x00) { 893 | goto yy59 894 | } 895 | goto yy69 896 | yy75: 897 | yyaccept = 2 898 | cursor++ 899 | marker = cursor 900 | yych = peek_next(input, cursor) 901 | switch (yych) { 902 | case 0x00: 903 | fallthrough 904 | case '\n': 905 | goto yy14 906 | default: 907 | goto yy70 908 | } 909 | yy76: 910 | cursor++ 911 | { 912 | return LexToken{uint32(began), uint32(cursor), TPLInc} 913 | } 914 | yy77: 915 | cursor++ 916 | { 917 | return LexToken{uint32(began), uint32(cursor), TPLDec} 918 | } 919 | yy78: 920 | cursor++ 921 | { 922 | return LexToken{uint32(began), uint32(cursor), TMinusGt} 923 | } 924 | yy79: 925 | yyaccept = 2 926 | cursor++ 927 | marker = cursor 928 | yych = peek_next(input, cursor) 929 | switch (yych) { 930 | case '\'': 931 | fallthrough 932 | case '0','1','2','3','4','5','6','7','8','9': 933 | goto yy79 934 | case 'B': 935 | goto yy111 936 | case 'E': 937 | fallthrough 938 | case 'e': 939 | goto yy84 940 | case 'F': 941 | fallthrough 942 | case 'f': 943 | goto yy112 944 | case 'L': 945 | fallthrough 946 | case 'l': 947 | goto yy65 948 | case 'b': 949 | goto yy113 950 | default: 951 | goto yy14 952 | } 953 | yy80: 954 | cursor++ 955 | yych = peek_next(input, cursor) 956 | switch (yych) { 957 | case '.': 958 | goto yy68 959 | default: 960 | goto yy59 961 | } 962 | yy81: 963 | cursor++ 964 | { 965 | for ;; { 966 | yych = peek_next(input, cursor) 967 | cursor++ 968 | if yych == 0 { 969 | return LexToken{0, 0, TEof} 970 | } else if yych == '*' { 971 | yych = peek_next(input, cursor) 972 | if yych == 0 { 973 | return LexToken{0, 0, TEof} 974 | } else if yych == '/' { 975 | cursor++; 976 | began = cursor 977 | goto lex_start 978 | } 979 | } 980 | } 981 | } 982 | yy82: 983 | cursor++ 984 | yych = peek_next(input, cursor) 985 | switch (yych) { 986 | case 0x00: 987 | fallthrough 988 | case '\n': 989 | goto yy3 990 | case '\\': 991 | goto yy114 992 | default: 993 | goto yy82 994 | } 995 | yy83: 996 | cursor++ 997 | yych = peek_next(input, cursor) 998 | switch (yych) { 999 | case '\'': 1000 | fallthrough 1001 | case '0','1': 1002 | goto yy115 1003 | default: 1004 | goto yy59 1005 | } 1006 | yy84: 1007 | cursor++ 1008 | yych = peek_next(input, cursor) 1009 | switch (yych) { 1010 | case '\'': 1011 | fallthrough 1012 | case '0','1','2','3','4','5','6','7','8','9': 1013 | goto yy108 1014 | case '+': 1015 | fallthrough 1016 | case '-': 1017 | goto yy116 1018 | default: 1019 | goto yy59 1020 | } 1021 | yy85: 1022 | cursor++ 1023 | yych = peek_next(input, cursor) 1024 | switch (yych) { 1025 | case 'L': 1026 | fallthrough 1027 | case 'U': 1028 | fallthrough 1029 | case 'Z': 1030 | fallthrough 1031 | case 'l': 1032 | fallthrough 1033 | case 'u': 1034 | fallthrough 1035 | case 'z': 1036 | goto yy85 1037 | default: 1038 | goto yy14 1039 | } 1040 | yy86: 1041 | cursor++ 1042 | yych = peek_next(input, cursor) 1043 | switch (yych) { 1044 | case '\'': 1045 | fallthrough 1046 | case '0','1','2','3','4','5','6','7','8','9': 1047 | goto yy117 1048 | default: 1049 | goto yy59 1050 | } 1051 | yy87: 1052 | cursor++ 1053 | yych = peek_next(input, cursor) 1054 | switch (yych) { 1055 | case '\'': 1056 | fallthrough 1057 | case '0','1','2','3','4','5','6','7','8','9': 1058 | fallthrough 1059 | case 'A','B','C','D','E','F': 1060 | fallthrough 1061 | case 'a','b','c','d','e','f': 1062 | goto yy118 1063 | case '.': 1064 | goto yy119 1065 | default: 1066 | goto yy59 1067 | } 1068 | yy88: 1069 | cursor++ 1070 | yych = peek_next(input, cursor) 1071 | switch (yych) { 1072 | case '=','>': 1073 | goto yy68 1074 | default: 1075 | goto yy62 1076 | } 1077 | yy89: 1078 | cursor++ 1079 | yych = peek_next(input, cursor) 1080 | switch (yych) { 1081 | case '=': 1082 | goto yy68 1083 | case '>': 1084 | goto yy61 1085 | default: 1086 | goto yy62 1087 | } 1088 | yy90: 1089 | cursor++ 1090 | { 1091 | cursor = lexRawQuoteLit(input, cursor) 1092 | return LexToken{uint32(began), uint32(cursor), TPLValue} 1093 | } 1094 | yy91: 1095 | cursor++ 1096 | yych = peek_next(input, cursor) 1097 | switch (yych) { 1098 | case '\n': 1099 | goto yy2 1100 | default: 1101 | goto yy59 1102 | } 1103 | yy92: 1104 | cursor++ 1105 | yych = peek_next(input, cursor) 1106 | switch (yych) { 1107 | case 'y': 1108 | goto yy120 1109 | default: 1110 | goto yy36 1111 | } 1112 | yy93: 1113 | cursor++ 1114 | yych = peek_next(input, cursor) 1115 | switch (yych) { 1116 | case 'a': 1117 | goto yy121 1118 | default: 1119 | goto yy36 1120 | } 1121 | yy94: 1122 | cursor++ 1123 | yych = peek_next(input, cursor) 1124 | switch (yych) { 1125 | case 'n': 1126 | goto yy122 1127 | default: 1128 | goto yy36 1129 | } 1130 | yy95: 1131 | cursor++ 1132 | yych = peek_next(input, cursor) 1133 | switch (yych) { 1134 | case 'u': 1135 | goto yy123 1136 | default: 1137 | goto yy36 1138 | } 1139 | yy96: 1140 | cursor++ 1141 | yych = peek_next(input, cursor) 1142 | switch (yych) { 1143 | case 'r': 1144 | goto yy124 1145 | default: 1146 | goto yy36 1147 | } 1148 | yy97: 1149 | cursor++ 1150 | yych = peek_next(input, cursor) 1151 | switch (yych) { 1152 | case 'n': 1153 | goto yy126 1154 | default: 1155 | goto yy36 1156 | } 1157 | yy98: 1158 | cursor++ 1159 | yych = peek_next(input, cursor) 1160 | switch (yych) { 1161 | case 'c': 1162 | goto yy127 1163 | default: 1164 | goto yy36 1165 | } 1166 | yy99: 1167 | cursor++ 1168 | yych = peek_next(input, cursor) 1169 | switch (yych) { 1170 | case 't': 1171 | goto yy128 1172 | default: 1173 | goto yy36 1174 | } 1175 | yy100: 1176 | cursor++ 1177 | yych = peek_next(input, cursor) 1178 | switch (yych) { 1179 | case 'o': 1180 | goto yy130 1181 | default: 1182 | goto yy36 1183 | } 1184 | yy101: 1185 | cursor++ 1186 | yych = peek_next(input, cursor) 1187 | switch (yych) { 1188 | case 'r': 1189 | goto yy131 1190 | default: 1191 | goto yy36 1192 | } 1193 | yy102: 1194 | cursor++ 1195 | yych = peek_next(input, cursor) 1196 | switch (yych) { 1197 | case 'd': 1198 | goto yy132 1199 | case 'i': 1200 | goto yy133 1201 | default: 1202 | goto yy36 1203 | } 1204 | yy103: 1205 | cursor++ 1206 | yych = peek_next(input, cursor) 1207 | switch (yych) { 1208 | case 'r': 1209 | goto yy128 1210 | default: 1211 | goto yy36 1212 | } 1213 | yy104: 1214 | cursor++ 1215 | yych = peek_next(input, cursor) 1216 | switch (yych) { 1217 | case 'i': 1218 | goto yy134 1219 | default: 1220 | goto yy36 1221 | } 1222 | yy105: 1223 | cursor++ 1224 | yych = peek_next(input, cursor) 1225 | switch (yych) { 1226 | case 'F': 1227 | goto yy135 1228 | default: 1229 | goto yy70 1230 | } 1231 | yy106: 1232 | yyaccept = 2 1233 | cursor++ 1234 | marker = cursor 1235 | yych = peek_next(input, cursor) 1236 | switch (yych) { 1237 | case 0x00: 1238 | fallthrough 1239 | case '\n': 1240 | goto yy14 1241 | case '1': 1242 | goto yy136 1243 | case '3': 1244 | goto yy137 1245 | case '6': 1246 | goto yy138 1247 | default: 1248 | goto yy70 1249 | } 1250 | yy107: 1251 | cursor++ 1252 | yych = peek_next(input, cursor) 1253 | switch (yych) { 1254 | case 'f': 1255 | goto yy135 1256 | default: 1257 | goto yy70 1258 | } 1259 | yy108: 1260 | yyaccept = 2 1261 | cursor++ 1262 | marker = cursor 1263 | yych = peek_next(input, cursor) 1264 | switch (yych) { 1265 | case '\'': 1266 | fallthrough 1267 | case '0','1','2','3','4','5','6','7','8','9': 1268 | goto yy108 1269 | case 'B': 1270 | goto yy111 1271 | case 'F': 1272 | fallthrough 1273 | case 'f': 1274 | goto yy112 1275 | case 'L': 1276 | fallthrough 1277 | case 'l': 1278 | goto yy65 1279 | case 'b': 1280 | goto yy113 1281 | default: 1282 | goto yy14 1283 | } 1284 | yy109: 1285 | cursor++ 1286 | yych = peek_next(input, cursor) 1287 | switch (yych) { 1288 | case '\'': 1289 | goto yy108 1290 | case '0','1','2','3','4','5','6','7','8','9': 1291 | goto yy110 1292 | default: 1293 | goto yy70 1294 | } 1295 | yy110: 1296 | yyaccept = 2 1297 | cursor++ 1298 | marker = cursor 1299 | yych = peek_next(input, cursor) 1300 | switch (yych) { 1301 | case 0x00: 1302 | fallthrough 1303 | case '\n': 1304 | goto yy14 1305 | case '\'': 1306 | goto yy108 1307 | case '0','1','2','3','4','5','6','7','8','9': 1308 | goto yy110 1309 | case 'B': 1310 | goto yy105 1311 | case 'F': 1312 | fallthrough 1313 | case 'f': 1314 | goto yy106 1315 | case 'L': 1316 | fallthrough 1317 | case 'l': 1318 | goto yy75 1319 | case '\\': 1320 | goto yy74 1321 | case 'b': 1322 | goto yy107 1323 | default: 1324 | goto yy69 1325 | } 1326 | yy111: 1327 | cursor++ 1328 | yych = peek_next(input, cursor) 1329 | switch (yych) { 1330 | case 'F': 1331 | goto yy139 1332 | default: 1333 | goto yy59 1334 | } 1335 | yy112: 1336 | yyaccept = 2 1337 | cursor++ 1338 | marker = cursor 1339 | yych = peek_next(input, cursor) 1340 | switch (yych) { 1341 | case '1': 1342 | goto yy140 1343 | case '3': 1344 | goto yy141 1345 | case '6': 1346 | goto yy142 1347 | default: 1348 | goto yy14 1349 | } 1350 | yy113: 1351 | cursor++ 1352 | yych = peek_next(input, cursor) 1353 | switch (yych) { 1354 | case 'f': 1355 | goto yy139 1356 | default: 1357 | goto yy59 1358 | } 1359 | yy114: 1360 | cursor++ 1361 | yych = peek_next(input, cursor) 1362 | switch (yych) { 1363 | case 0x00: 1364 | goto yy3 1365 | case '\n': 1366 | goto yy2 1367 | case '\r': 1368 | goto yy143 1369 | case '\\': 1370 | goto yy114 1371 | default: 1372 | goto yy82 1373 | } 1374 | yy115: 1375 | cursor++ 1376 | yych = peek_next(input, cursor) 1377 | switch (yych) { 1378 | case '\'': 1379 | fallthrough 1380 | case '0','1': 1381 | goto yy115 1382 | case 'L': 1383 | fallthrough 1384 | case 'U': 1385 | fallthrough 1386 | case 'Z': 1387 | fallthrough 1388 | case 'l': 1389 | fallthrough 1390 | case 'u': 1391 | fallthrough 1392 | case 'z': 1393 | goto yy85 1394 | case 'n': 1395 | goto yy65 1396 | default: 1397 | goto yy14 1398 | } 1399 | yy116: 1400 | cursor++ 1401 | yych = peek_next(input, cursor) 1402 | switch (yych) { 1403 | case '\'': 1404 | fallthrough 1405 | case '0','1','2','3','4','5','6','7','8','9': 1406 | goto yy108 1407 | default: 1408 | goto yy59 1409 | } 1410 | yy117: 1411 | cursor++ 1412 | yych = peek_next(input, cursor) 1413 | switch (yych) { 1414 | case '\'': 1415 | fallthrough 1416 | case '0','1','2','3','4','5','6','7','8','9': 1417 | goto yy117 1418 | case 'L': 1419 | fallthrough 1420 | case 'U': 1421 | fallthrough 1422 | case 'Z': 1423 | fallthrough 1424 | case 'l': 1425 | fallthrough 1426 | case 'u': 1427 | fallthrough 1428 | case 'z': 1429 | goto yy85 1430 | case 'n': 1431 | goto yy65 1432 | default: 1433 | goto yy14 1434 | } 1435 | yy118: 1436 | yyaccept = 2 1437 | cursor++ 1438 | marker = cursor 1439 | yych = peek_next(input, cursor) 1440 | switch (yych) { 1441 | case '\'': 1442 | fallthrough 1443 | case '0','1','2','3','4','5','6','7','8','9': 1444 | fallthrough 1445 | case 'A','B','C','D','E','F': 1446 | fallthrough 1447 | case 'a','b','c','d','e','f': 1448 | goto yy118 1449 | case '.': 1450 | goto yy144 1451 | case 'L': 1452 | fallthrough 1453 | case 'U': 1454 | fallthrough 1455 | case 'Z': 1456 | fallthrough 1457 | case 'l': 1458 | fallthrough 1459 | case 'u': 1460 | fallthrough 1461 | case 'z': 1462 | goto yy85 1463 | case 'P': 1464 | fallthrough 1465 | case 'p': 1466 | goto yy146 1467 | case 'n': 1468 | goto yy65 1469 | default: 1470 | goto yy14 1471 | } 1472 | yy119: 1473 | cursor++ 1474 | yych = peek_next(input, cursor) 1475 | switch (yych) { 1476 | case 'P': 1477 | fallthrough 1478 | case 'p': 1479 | goto yy59 1480 | default: 1481 | goto yy145 1482 | } 1483 | yy120: 1484 | cursor++ 1485 | yych = peek_next(input, cursor) 1486 | switch (yych) { 1487 | case 'n': 1488 | goto yy147 1489 | default: 1490 | goto yy36 1491 | } 1492 | yy121: 1493 | cursor++ 1494 | yych = peek_next(input, cursor) 1495 | switch (yych) { 1496 | case 's': 1497 | goto yy148 1498 | default: 1499 | goto yy36 1500 | } 1501 | yy122: 1502 | cursor++ 1503 | yych = peek_next(input, cursor) 1504 | switch (yych) { 1505 | case 's': 1506 | goto yy149 1507 | default: 1508 | goto yy36 1509 | } 1510 | yy123: 1511 | cursor++ 1512 | yych = peek_next(input, cursor) 1513 | switch (yych) { 1514 | case 'm': 1515 | goto yy150 1516 | default: 1517 | goto yy36 1518 | } 1519 | yy124: 1520 | cursor++ 1521 | yych = peek_next(input, cursor) 1522 | switch (yych) { 1523 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 1524 | fallthrough 1525 | case ':',';','<','=','>','?','@': 1526 | fallthrough 1527 | case '[','\\',']','^': 1528 | fallthrough 1529 | case '`': 1530 | fallthrough 1531 | case '{','|','}','~',0x7F: 1532 | fallthrough 1533 | case 0x85: 1534 | goto yy125 1535 | default: 1536 | goto yy35 1537 | } 1538 | yy125: 1539 | { 1540 | return LexToken{uint32(began), uint32(cursor), TFunctionForWhileJsclass} 1541 | } 1542 | yy126: 1543 | cursor++ 1544 | yych = peek_next(input, cursor) 1545 | switch (yych) { 1546 | case 'c': 1547 | goto yy152 1548 | default: 1549 | goto yy36 1550 | } 1551 | yy127: 1552 | cursor++ 1553 | yych = peek_next(input, cursor) 1554 | switch (yych) { 1555 | case 'l': 1556 | goto yy153 1557 | default: 1558 | goto yy36 1559 | } 1560 | yy128: 1561 | cursor++ 1562 | yych = peek_next(input, cursor) 1563 | switch (yych) { 1564 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 1565 | fallthrough 1566 | case ':',';','<','=','>','?','@': 1567 | fallthrough 1568 | case '[','\\',']','^': 1569 | fallthrough 1570 | case '`': 1571 | fallthrough 1572 | case '{','|','}','~',0x7F: 1573 | fallthrough 1574 | case 0x85: 1575 | goto yy129 1576 | default: 1577 | goto yy35 1578 | } 1579 | yy129: 1580 | { 1581 | return LexToken{uint32(began), uint32(cursor), TJsOnly} 1582 | } 1583 | yy130: 1584 | cursor++ 1585 | yych = peek_next(input, cursor) 1586 | switch (yych) { 1587 | case 't': 1588 | goto yy154 1589 | default: 1590 | goto yy36 1591 | } 1592 | yy131: 1593 | cursor++ 1594 | yych = peek_next(input, cursor) 1595 | switch (yych) { 1596 | case 'u': 1597 | goto yy155 1598 | default: 1599 | goto yy36 1600 | } 1601 | yy132: 1602 | cursor++ 1603 | yych = peek_next(input, cursor) 1604 | switch (yych) { 1605 | case 'e': 1606 | goto yy156 1607 | default: 1608 | goto yy36 1609 | } 1610 | yy133: 1611 | cursor++ 1612 | yych = peek_next(input, cursor) 1613 | switch (yych) { 1614 | case 'o': 1615 | goto yy157 1616 | default: 1617 | goto yy36 1618 | } 1619 | yy134: 1620 | cursor++ 1621 | yych = peek_next(input, cursor) 1622 | switch (yych) { 1623 | case 'l': 1624 | goto yy158 1625 | default: 1626 | goto yy36 1627 | } 1628 | yy135: 1629 | cursor++ 1630 | yych = peek_next(input, cursor) 1631 | switch (yych) { 1632 | case '1': 1633 | goto yy159 1634 | default: 1635 | goto yy70 1636 | } 1637 | yy136: 1638 | cursor++ 1639 | yych = peek_next(input, cursor) 1640 | switch (yych) { 1641 | case '2': 1642 | goto yy160 1643 | case '6': 1644 | goto yy75 1645 | default: 1646 | goto yy70 1647 | } 1648 | yy137: 1649 | cursor++ 1650 | yych = peek_next(input, cursor) 1651 | switch (yych) { 1652 | case '2': 1653 | goto yy75 1654 | default: 1655 | goto yy70 1656 | } 1657 | yy138: 1658 | cursor++ 1659 | yych = peek_next(input, cursor) 1660 | switch (yych) { 1661 | case '4': 1662 | goto yy75 1663 | default: 1664 | goto yy70 1665 | } 1666 | yy139: 1667 | cursor++ 1668 | yych = peek_next(input, cursor) 1669 | switch (yych) { 1670 | case '1': 1671 | goto yy161 1672 | default: 1673 | goto yy59 1674 | } 1675 | yy140: 1676 | cursor++ 1677 | yych = peek_next(input, cursor) 1678 | switch (yych) { 1679 | case '2': 1680 | goto yy162 1681 | case '6': 1682 | goto yy65 1683 | default: 1684 | goto yy59 1685 | } 1686 | yy141: 1687 | cursor++ 1688 | yych = peek_next(input, cursor) 1689 | switch (yych) { 1690 | case '2': 1691 | goto yy65 1692 | default: 1693 | goto yy59 1694 | } 1695 | yy142: 1696 | cursor++ 1697 | yych = peek_next(input, cursor) 1698 | switch (yych) { 1699 | case '4': 1700 | goto yy65 1701 | default: 1702 | goto yy59 1703 | } 1704 | yy143: 1705 | cursor++ 1706 | yych = peek_next(input, cursor) 1707 | switch (yych) { 1708 | case 0x00: 1709 | goto yy3 1710 | case '\n': 1711 | goto yy2 1712 | case '\\': 1713 | goto yy114 1714 | default: 1715 | goto yy82 1716 | } 1717 | yy144: 1718 | cursor++ 1719 | yych = peek_next(input, cursor) 1720 | yy145: 1721 | switch (yych) { 1722 | case '\'': 1723 | fallthrough 1724 | case '0','1','2','3','4','5','6','7','8','9': 1725 | fallthrough 1726 | case 'A','B','C','D','E','F': 1727 | fallthrough 1728 | case 'a','b','c','d','e','f': 1729 | goto yy144 1730 | case 'P': 1731 | fallthrough 1732 | case 'p': 1733 | goto yy146 1734 | default: 1735 | goto yy59 1736 | } 1737 | yy146: 1738 | cursor++ 1739 | yych = peek_next(input, cursor) 1740 | switch (yych) { 1741 | case '\'': 1742 | fallthrough 1743 | case '0','1','2','3','4','5','6','7','8','9': 1744 | fallthrough 1745 | case 'A','B','C','D','E','F': 1746 | fallthrough 1747 | case 'a','b','c','d','e','f': 1748 | goto yy163 1749 | case '+': 1750 | fallthrough 1751 | case '-': 1752 | goto yy164 1753 | default: 1754 | goto yy59 1755 | } 1756 | yy147: 1757 | cursor++ 1758 | yych = peek_next(input, cursor) 1759 | switch (yych) { 1760 | case 'c': 1761 | goto yy128 1762 | default: 1763 | goto yy36 1764 | } 1765 | yy148: 1766 | cursor++ 1767 | yych = peek_next(input, cursor) 1768 | switch (yych) { 1769 | case 's': 1770 | goto yy150 1771 | default: 1772 | goto yy36 1773 | } 1774 | yy149: 1775 | cursor++ 1776 | yych = peek_next(input, cursor) 1777 | switch (yych) { 1778 | case 't': 1779 | goto yy165 1780 | default: 1781 | goto yy36 1782 | } 1783 | yy150: 1784 | cursor++ 1785 | yych = peek_next(input, cursor) 1786 | switch (yych) { 1787 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 1788 | fallthrough 1789 | case ':',';','<','=','>','?','@': 1790 | fallthrough 1791 | case '[','\\',']','^': 1792 | fallthrough 1793 | case '`': 1794 | fallthrough 1795 | case '{','|','}','~',0x7F: 1796 | fallthrough 1797 | case 0x85: 1798 | goto yy151 1799 | default: 1800 | goto yy35 1801 | } 1802 | yy151: 1803 | { 1804 | return LexToken{uint32(began), uint32(cursor), TStructUnionEnumClass} 1805 | } 1806 | yy152: 1807 | cursor++ 1808 | yych = peek_next(input, cursor) 1809 | switch (yych) { 1810 | case 't': 1811 | goto yy167 1812 | default: 1813 | goto yy36 1814 | } 1815 | yy153: 1816 | cursor++ 1817 | yych = peek_next(input, cursor) 1818 | switch (yych) { 1819 | case 'a': 1820 | goto yy168 1821 | default: 1822 | goto yy36 1823 | } 1824 | yy154: 1825 | cursor++ 1826 | yych = peek_next(input, cursor) 1827 | switch (yych) { 1828 | case 'e': 1829 | goto yy169 1830 | default: 1831 | goto yy36 1832 | } 1833 | yy155: 1834 | cursor++ 1835 | yych = peek_next(input, cursor) 1836 | switch (yych) { 1837 | case 'c': 1838 | goto yy171 1839 | default: 1840 | goto yy36 1841 | } 1842 | yy156: 1843 | cursor++ 1844 | yych = peek_next(input, cursor) 1845 | switch (yych) { 1846 | case 'f': 1847 | goto yy172 1848 | default: 1849 | goto yy36 1850 | } 1851 | yy157: 1852 | cursor++ 1853 | yych = peek_next(input, cursor) 1854 | switch (yych) { 1855 | case 'n': 1856 | goto yy150 1857 | default: 1858 | goto yy36 1859 | } 1860 | yy158: 1861 | cursor++ 1862 | yych = peek_next(input, cursor) 1863 | switch (yych) { 1864 | case 'e': 1865 | goto yy124 1866 | default: 1867 | goto yy36 1868 | } 1869 | yy159: 1870 | cursor++ 1871 | yych = peek_next(input, cursor) 1872 | switch (yych) { 1873 | case '6': 1874 | goto yy75 1875 | default: 1876 | goto yy70 1877 | } 1878 | yy160: 1879 | cursor++ 1880 | yych = peek_next(input, cursor) 1881 | switch (yych) { 1882 | case '8': 1883 | goto yy75 1884 | default: 1885 | goto yy70 1886 | } 1887 | yy161: 1888 | cursor++ 1889 | yych = peek_next(input, cursor) 1890 | switch (yych) { 1891 | case '6': 1892 | goto yy65 1893 | default: 1894 | goto yy59 1895 | } 1896 | yy162: 1897 | cursor++ 1898 | yych = peek_next(input, cursor) 1899 | switch (yych) { 1900 | case '8': 1901 | goto yy65 1902 | default: 1903 | goto yy59 1904 | } 1905 | yy163: 1906 | cursor++ 1907 | yych = peek_next(input, cursor) 1908 | switch (yych) { 1909 | case '\'': 1910 | fallthrough 1911 | case '0','1','2','3','4','5','6','7','8','9': 1912 | fallthrough 1913 | case 'A','B','C','D','E','F': 1914 | fallthrough 1915 | case 'a','b','c','d','e','f': 1916 | goto yy163 1917 | case 'L': 1918 | fallthrough 1919 | case 'l': 1920 | goto yy65 1921 | default: 1922 | goto yy14 1923 | } 1924 | yy164: 1925 | cursor++ 1926 | yych = peek_next(input, cursor) 1927 | switch (yych) { 1928 | case '\'': 1929 | fallthrough 1930 | case '0','1','2','3','4','5','6','7','8','9': 1931 | fallthrough 1932 | case 'A','B','C','D','E','F': 1933 | fallthrough 1934 | case 'a','b','c','d','e','f': 1935 | goto yy163 1936 | default: 1937 | goto yy59 1938 | } 1939 | yy165: 1940 | cursor++ 1941 | yych = peek_next(input, cursor) 1942 | switch (yych) { 1943 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 1944 | fallthrough 1945 | case ':',';','<','=','>','?','@': 1946 | fallthrough 1947 | case '[','\\',']','^': 1948 | fallthrough 1949 | case '`': 1950 | fallthrough 1951 | case '{','|','}','~',0x7F: 1952 | fallthrough 1953 | case 0x85: 1954 | goto yy166 1955 | default: 1956 | goto yy35 1957 | } 1958 | yy166: 1959 | { 1960 | return LexToken{uint32(began), uint32(cursor), TConst} 1961 | } 1962 | yy167: 1963 | cursor++ 1964 | yych = peek_next(input, cursor) 1965 | switch (yych) { 1966 | case 'i': 1967 | goto yy173 1968 | default: 1969 | goto yy36 1970 | } 1971 | yy168: 1972 | cursor++ 1973 | yych = peek_next(input, cursor) 1974 | switch (yych) { 1975 | case 's': 1976 | goto yy174 1977 | default: 1978 | goto yy36 1979 | } 1980 | yy169: 1981 | cursor++ 1982 | yych = peek_next(input, cursor) 1983 | switch (yych) { 1984 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 1985 | fallthrough 1986 | case ':',';','<','=','>','?','@': 1987 | fallthrough 1988 | case '[','\\',']','^': 1989 | fallthrough 1990 | case '`': 1991 | fallthrough 1992 | case '{','|','}','~',0x7F: 1993 | fallthrough 1994 | case 0x85: 1995 | goto yy170 1996 | default: 1997 | goto yy35 1998 | } 1999 | yy170: 2000 | { 2001 | return LexToken{uint32(began), uint32(cursor), TQuote} 2002 | } 2003 | yy171: 2004 | cursor++ 2005 | yych = peek_next(input, cursor) 2006 | switch (yych) { 2007 | case 't': 2008 | goto yy150 2009 | default: 2010 | goto yy36 2011 | } 2012 | yy172: 2013 | cursor++ 2014 | yych = peek_next(input, cursor) 2015 | switch (yych) { 2016 | case 'i': 2017 | goto yy175 2018 | default: 2019 | goto yy36 2020 | } 2021 | yy173: 2022 | cursor++ 2023 | yych = peek_next(input, cursor) 2024 | switch (yych) { 2025 | case 'o': 2026 | goto yy176 2027 | default: 2028 | goto yy36 2029 | } 2030 | yy174: 2031 | cursor++ 2032 | yych = peek_next(input, cursor) 2033 | switch (yych) { 2034 | case 's': 2035 | goto yy177 2036 | default: 2037 | goto yy36 2038 | } 2039 | yy175: 2040 | cursor++ 2041 | yych = peek_next(input, cursor) 2042 | switch (yych) { 2043 | case 'n': 2044 | goto yy179 2045 | default: 2046 | goto yy36 2047 | } 2048 | yy176: 2049 | cursor++ 2050 | yych = peek_next(input, cursor) 2051 | switch (yych) { 2052 | case 'n': 2053 | goto yy124 2054 | default: 2055 | goto yy36 2056 | } 2057 | yy177: 2058 | cursor++ 2059 | yych = peek_next(input, cursor) 2060 | switch (yych) { 2061 | case 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,'\t','\n','\v','\f','\r',0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/': 2062 | fallthrough 2063 | case ':',';','<','=','>','?','@': 2064 | fallthrough 2065 | case '[','\\',']','^': 2066 | fallthrough 2067 | case '`': 2068 | fallthrough 2069 | case '{','|','}','~',0x7F: 2070 | fallthrough 2071 | case 0x85: 2072 | goto yy178 2073 | default: 2074 | goto yy35 2075 | } 2076 | yy178: 2077 | { 2078 | input[began] = ' ' 2079 | input[began+1] = ' ' 2080 | return LexToken{uint32(began+2), uint32(cursor), TFunctionForWhileJsclass} 2081 | } 2082 | yy179: 2083 | cursor++ 2084 | yych = peek_next(input, cursor) 2085 | switch (yych) { 2086 | case 'e': 2087 | goto yy180 2088 | default: 2089 | goto yy36 2090 | } 2091 | yy180: 2092 | cursor++ 2093 | yych = peek_next(input, cursor) 2094 | switch (yych) { 2095 | case 'd': 2096 | goto yy128 2097 | default: 2098 | goto yy36 2099 | } 2100 | } 2101 | 2102 | } 2103 | 2104 | func lexRegexpLit(input []byte, cursor uint) LexToken { 2105 | var began uint = cursor 2106 | var marker uint = 0 2107 | peek_next := func(str []byte, i uint) byte { 2108 | if i < uint(len(str)) { 2109 | return str[i] 2110 | } else { 2111 | return 0 2112 | } 2113 | } 2114 | 2115 | { 2116 | var yych byte 2117 | yych = peek_next(input, cursor) 2118 | switch (yych) { 2119 | case 0x00: 2120 | goto yy182 2121 | case '/': 2122 | goto yy185 2123 | default: 2124 | goto yy183 2125 | } 2126 | yy182: 2127 | cursor++ 2128 | { 2129 | return LexToken{0, 0, TEof} 2130 | } 2131 | yy183: 2132 | cursor++ 2133 | yy184: 2134 | { 2135 | return LexToken{0, 0, TInvalid} 2136 | } 2137 | yy185: 2138 | cursor++ 2139 | marker = cursor 2140 | yych = peek_next(input, cursor) 2141 | switch (yych) { 2142 | case 0x00: 2143 | fallthrough 2144 | case '\n': 2145 | fallthrough 2146 | case '/': 2147 | goto yy184 2148 | default: 2149 | goto yy187 2150 | } 2151 | yy186: 2152 | cursor++ 2153 | yych = peek_next(input, cursor) 2154 | yy187: 2155 | switch (yych) { 2156 | case 0x00: 2157 | fallthrough 2158 | case '\n': 2159 | goto yy188 2160 | case '/': 2161 | goto yy191 2162 | case '[': 2163 | goto yy189 2164 | case '\\': 2165 | goto yy190 2166 | default: 2167 | goto yy186 2168 | } 2169 | yy188: 2170 | cursor = marker 2171 | goto yy184 2172 | yy189: 2173 | cursor++ 2174 | yych = peek_next(input, cursor) 2175 | switch (yych) { 2176 | case 0x00: 2177 | fallthrough 2178 | case '\n': 2179 | fallthrough 2180 | case '/': 2181 | goto yy188 2182 | case '\\': 2183 | goto yy193 2184 | case ']': 2185 | goto yy186 2186 | default: 2187 | goto yy189 2188 | } 2189 | yy190: 2190 | cursor++ 2191 | yych = peek_next(input, cursor) 2192 | if (yych <= 0x00) { 2193 | goto yy188 2194 | } 2195 | goto yy186 2196 | yy191: 2197 | cursor++ 2198 | yych = peek_next(input, cursor) 2199 | switch (yych) { 2200 | case 'd': 2201 | fallthrough 2202 | case 'g': 2203 | fallthrough 2204 | case 'i': 2205 | fallthrough 2206 | case 'm': 2207 | fallthrough 2208 | case 's': 2209 | fallthrough 2210 | case 'u','v': 2211 | fallthrough 2212 | case 'y': 2213 | goto yy191 2214 | default: 2215 | goto yy192 2216 | } 2217 | yy192: 2218 | { 2219 | return LexToken{uint32(began), uint32(cursor), TOther} 2220 | } 2221 | yy193: 2222 | cursor++ 2223 | yych = peek_next(input, cursor) 2224 | if (yych <= 0x00) { 2225 | goto yy188 2226 | } 2227 | goto yy189 2228 | } 2229 | 2230 | } 2231 | 2232 | func lexTickLit(input []byte, cursor uint) LexToken { 2233 | var began uint = cursor 2234 | var ch byte 2235 | for ;; { 2236 | if cursor < uint(len(input)) { 2237 | ch = input[cursor] 2238 | cursor++ 2239 | } else { 2240 | return LexToken{0, 0, TInvalid} 2241 | } 2242 | if ch == '\\' { 2243 | if cursor < uint(len(input)) { 2244 | cursor++ 2245 | } else { 2246 | return LexToken{0, 0, TInvalid} 2247 | } 2248 | } else if ch == '`' { 2249 | if (began+1) == cursor { 2250 | return LexToken{uint32(began), uint32(cursor), TTick} 2251 | } else { 2252 | return LexToken{uint32(began), uint32(cursor-1), TOther} 2253 | } 2254 | } else if (ch == '$') { 2255 | if cursor < uint(len(input)) { 2256 | ch = input[cursor] 2257 | cursor++ 2258 | } else { 2259 | return LexToken{0, 0, TInvalid} 2260 | } 2261 | if (ch == '{') { 2262 | if (began+2) == cursor { 2263 | return LexToken{uint32(began), uint32(cursor), TDollarLBrace} 2264 | } else { 2265 | return LexToken{uint32(began), uint32(cursor-2), TOther} 2266 | } 2267 | } 2268 | } 2269 | } 2270 | } --------------------------------------------------------------------------------