Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(comments): parser #13

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cparse
docsonnet
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.PHONY: docsonnet cparse

VERSION := $(shell git describe --tags --dirty --always)

docsonnet: pkged.go
go build -o docsonnet -ldflags "-X main.Version=dev-$(VERSION)" .

cparse: pkged.go
go build -o cparse -ldflags "-X main.Version=dev-$(VERSION)" ./cmd/cparse

pkged.go: doc-util/main.libsonnet pkg/comments/dsl.libsonnet
rm -f cmd/cparse/pkged.go
pkger
ln -s $(PWD)/pkged.go cmd/cparse/pkged.go
9 changes: 6 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import (
"github.com/sh0rez/docsonnet/pkg/render"
)

var Version = "dev"

func main() {
log.SetFlags(0)

root := &cli.Command{
Use: "docsonnet <file>",
Short: "Utility to parse and transform Jsonnet code that uses the docsonnet extension",
Args: cli.ArgsExact(1),
Use: "docsonnet <file>",
Short: "Utility to parse and transform Jsonnet code that uses the docsonnet extension",
Args: cli.ArgsExact(1),
Version: Version,
}

dir := root.Flags().StringP("output", "o", "docs", "directory to write the .md files to")
Expand Down
145 changes: 145 additions & 0 deletions pkg/comments/comments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package comments

import (
"fmt"
"io/ioutil"
"strings"

"github.com/google/go-jsonnet"
"github.com/google/go-jsonnet/formatter"
"github.com/markbates/pkger"
)

type Blocks []string

func (b Blocks) String() string {
return strings.Join(b, "---\n")
}

func Transform(filename, data string) (string, error) {
return TransformStaged(filename, data, StageFormat)
}

type Stage int

const (
StageScan Stage = iota
StageTranslate
StageEval
StageJoin
StageFormat
)

func TransformStaged(filename, data string, stage Stage) (string, error) {
// Stage 0: Scan for comments
blocks, err := Scan(data)
if err != nil {
return "", err
}
if stage == StageScan {
return blocks.String(), nil
}

// Stage 1: Translate to DSL
for i := range blocks {
blocks[i] = Translate(blocks[i])
}
if stage == StageTranslate {
return blocks.String(), nil
}

// Stage 2: Eval DSL
for i := range blocks {
blocks[i], err = Eval(blocks[i])
if err != nil {
return "", err
}
}
if stage == StageEval {
return blocks.String(), nil
}

// Stage 3: Join
joined := data + Join(blocks)
if stage == StageJoin {
return joined, nil
}

// Stage 4: Format
formatted, err := formatter.Format(filename, joined, formatter.DefaultOptions())
if err != nil {
return "", err
}
return formatted, nil
}

// Scan extracts comment blocks from the Jsonnet document
func Scan(doc string) (Blocks, error) {
doc, err := formatter.Format("", doc, formatter.Options{
CommentStyle: formatter.CommentStyleHash,
})
if err != nil {
return nil, err
}

var blocks []string
block := ""

for _, l := range strings.Split(doc, "\n") {
l := strings.TrimSpace(l)
if !strings.HasPrefix(l, "#") {
if block != "" {
blocks = append(blocks, block)
block = ""
}
continue
}

block += l + "\n"
}

return blocks, nil
}

// Translate converts the comment syntax into Jsonnet DSL invocations
func Translate(block string) string {
block = strings.Replace(block, "# @", "+ ", -1)
block = strings.Replace(block, "# ", "", -1)
return block
}

// Eval converts the block into an actual object, by evaluating it in the
// context of our DSL
func Eval(block string) (string, error) {
dsl := loadDSL()

vm := jsonnet.MakeVM()
out, err := vm.EvaluateSnippet("", dsl+"\n"+block)
if err != nil {
return "", err
}

return out, nil
}

// Join chains multiple comment blocks into a single patch
func Join(blocks Blocks) string {
s := ""
for _, b := range blocks {
s += "+ " + b
}
return s
}

func loadDSL() string {
p, err := pkger.Open("/pkg/comments/dsl.libsonnet")
if err != nil {
// This must work. panic if not
panic(fmt.Errorf("Loading embedded file: %s. This build appears broken", err))
}
data, err := ioutil.ReadAll(p)
if err != nil {
panic(fmt.Errorf("Loading embedded file: %s. This build appears broken", err))
}
return string(data)
}
52 changes: 52 additions & 0 deletions pkg/comments/dsl.libsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
local d = import 'doc-util/main.libsonnet';

local pkg(name, url) = {
NAME:: {
TYPE:: { help: "" },
name: name,
'import': url,
help: self.TYPE.help,
},
"#": self.NAME,
};

local dType(kind) = function(name) {
// NAME is used to mix into the docsonnet field without knowing it's name
NAME:: {
// TYPE is used to mix into {function,object,value} without knowing what it is
TYPE:: {},
[kind]: self.TYPE,
},
['#' + name]: self.NAME,
};

local fn = dType('function'),
obj = dType('object');

local val(name, type, default=null) = dType('value') + {
NAME+: { TYPE+: {
type: type,
default: default,
} },
};

local arg(name, type, default=null) = {
NAME+: { TYPE+: {
args+: [{
name: name,
type: type,
default: default,
}],
} },
};

local desc(help) = {
NAME+: { TYPE+: {
help: help,
} },
};

local string = 'string',
object = 'object';

{}
Loading