├── .gitignore ├── .github └── workflows │ └── ci.yaml ├── LICENSE ├── README.md └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: unit 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | name: all 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: set up 15 | uses: actions/setup-go@v1 16 | with: 17 | go-version: 1.13 18 | id: go 19 | - name: checkout 20 | uses: actions/checkout@v1 21 | - name: build 22 | run: go build -o main ./main.go 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kentaro IMAJO 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Echotmpl 2 | Generic template engine for languages with semicolons (C, C++, and etc.). 3 | Echotmpl converts text blocks into `echo("...");` 4 | for any language that can accept it. 5 | 6 | ## Usage 7 | 8 | ``` 9 | $ echotempl < example.tmpl > example.cpp 10 | ``` 11 | 12 | ## Example 13 | 14 | Input: 15 | ```php 16 | 18 | #include 19 | 20 | std::string List(const std::vector& v) { 21 | std::string output; 22 | auto echo = [&output](const std::string& s) { output += s; }; 23 | ?> 24 | 25 | 34 | 35 | 43 | #include 44 | 45 | std::string List(const std::vector& v) { 46 | std::string output; 47 | auto echo = [&output](const std::string& s) { output += s; }; 48 | echo("\x0a\x0a
    \x0a"); 49 | 50 | for (const std::string& s : v) { 51 | echo("\x0a
  • Item "); 52 | echo(s); 53 | echo("
  • \x0a"); 54 | 55 | } 56 | echo("\x0a
\x0a\x0a"); 57 | return output; 58 | } 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "strconv" 10 | ) 11 | 12 | // Convert expands a template. 13 | func Convert(tmpl []byte) ([]byte, error) { 14 | output := make([]byte, 0, len(tmpl)*2) 15 | 16 | // isText is true iff tmpl[pos] is in text mode (not in code). 17 | isText := true 18 | // The code block is in the echo mode (). 19 | isEchoCode := false 20 | // Whether echo function starts. 21 | isEchoing := false 22 | 23 | pos := 0 24 | hasPrefix := func(prefix string) bool { 25 | return bytes.HasPrefix(tmpl[pos:], []byte(prefix)) 26 | } 27 | for ; pos < len(tmpl); pos++ { 28 | if isText { 29 | leftEchoBracket := hasPrefix("") { 64 | pos += 1 65 | if isEchoCode { 66 | output = append(output, []byte(");\n")...) 67 | } 68 | isText = true 69 | isEchoing = false 70 | isEchoCode = false 71 | } else { 72 | output = append(output, tmpl[pos]) 73 | } 74 | } 75 | 76 | return output, nil 77 | } 78 | 79 | func main() { 80 | input, err := ioutil.ReadAll(os.Stdin) 81 | if err != nil { 82 | log.Fatalf("failed to read: %+v", err) 83 | } 84 | output, err := Convert(input) 85 | if err != nil { 86 | log.Fatalf("failed to convert: %+v", err) 87 | } 88 | fmt.Printf("%s", output) 89 | } 90 | --------------------------------------------------------------------------------