diff --git a/public/parsing.wasm b/public/parsing.wasm
index 98484b9..bda6c88 100755
Binary files a/public/parsing.wasm and b/public/parsing.wasm differ
diff --git a/src/parsing/embed.go b/src/parsing/embed.go
index c342eba..0073686 100644
--- a/src/parsing/embed.go
+++ b/src/parsing/embed.go
@@ -104,15 +104,19 @@ func extract(re *regexp.Regexp, src []byte, subexpName string) []byte {
return m[re.SubexpIndex(subexpName)]
}
-func extractMap(re *regexp.Regexp, src []byte, subexpNames ...string) map[string][]byte {
+func extractMap(re *regexp.Regexp, src []byte) map[string][]byte {
m := re.FindSubmatch(src)
if m == nil {
return nil
}
res := make(map[string][]byte)
- for _, name := range subexpNames {
- res[name] = m[re.SubexpIndex(name)]
+ for _, name := range re.SubexpNames() {
+ if name != "" {
+ i := re.SubexpIndex(name)
+ res[name] = m[i]
+ }
}
+ res["all"] = m[0]
return res
}
@@ -124,15 +128,22 @@ func extractAll(re *regexp.Regexp, src []byte, subexpName string) [][]byte {
return m[re.SubexpIndex(subexpName)]
}
-func extractAllMap(re *regexp.Regexp, src []byte, subexpNames ...string) map[string][][]byte {
+func extractAllMap(re *regexp.Regexp, src []byte) map[string][][]byte {
m := re.FindAllSubmatch(src, -1)
if m == nil {
return nil
}
res := make(map[string][][]byte)
- for _, name := range subexpNames {
- res[name] = m[re.SubexpIndex(name)]
+ for i, name := range re.SubexpNames() {
+ if name != "" {
+ var vals [][]byte
+ for _, specificMatch := range m {
+ vals = append(vals, specificMatch[i])
+ }
+ res[name] = vals
+ }
}
+ res["all"] = m[0]
return res
}
diff --git a/src/parsing/ggcode.go b/src/parsing/ggcode.go
index 4c42895..405ec80 100644
--- a/src/parsing/ggcode.go
+++ b/src/parsing/ggcode.go
@@ -2,8 +2,10 @@ package parsing
import (
"bytes"
+ "fmt"
"regexp"
+ "git.handmade.network/hmn/hmn/src/hmnurl"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
gast "github.com/yuin/goldmark/ast"
@@ -38,6 +40,19 @@ import (
type ggcodeRenderer = func(w util.BufWriter, n *ggcodeNode, entering bool) error
var ggcodeTags = map[string]ggcodeRenderer{
+ "glossary": func(w util.BufWriter, n *ggcodeNode, entering bool) error {
+ if entering {
+ term, _ := n.Args["term"]
+ w.WriteString(fmt.Sprintf(
+ ``,
+ hmnurl.BuildEducationGlossary(term),
+ term,
+ ))
+ } else {
+ w.WriteString("")
+ }
+ return nil
+ },
"resource": func(w util.BufWriter, n *ggcodeNode, entering bool) error {
if entering {
w.WriteString("a node :o")
@@ -53,25 +68,28 @@ var ggcodeTags = map[string]ggcodeRenderer{
// ----------------------
var reGGCodeBlockOpen = regexp.MustCompile(`^!!!(?P[a-zA-Z0-9-_]+)(\{(?P.*?)\})?$`)
-var reGGCodeBlockArgs = regexp.MustCompile(`(?P[a-zA-Z0-9-_]+)="(?P.*?)"`)
+var reGGCodeInline = regexp.MustCompile(`^!!(?P[a-zA-Z0-9-_]+)(\{(?P.*?)\})?(\((?P.*?)\))?`)
+var reGGCodeArgs = regexp.MustCompile(`(?P[a-zA-Z0-9-_]+)="(?P.*?)"`)
-type ggcodeParserBlock struct {
+// Block parser stuff
+
+type ggcodeBlockParser struct {
Preview bool
}
-var _ parser.BlockParser = ggcodeParserBlock{}
+var _ parser.BlockParser = ggcodeBlockParser{}
-func (s ggcodeParserBlock) Trigger() []byte {
- return []byte("!!!")
+func (s ggcodeBlockParser) Trigger() []byte {
+ return []byte("!")
}
-func (s ggcodeParserBlock) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
+func (s ggcodeBlockParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
restOfLine, _ := reader.PeekLine()
- if match := extractMap(reGGCodeBlockOpen, bytes.TrimSpace(restOfLine), "name", "args"); match != nil {
+ if match := extractMap(reGGCodeBlockOpen, bytes.TrimSpace(restOfLine)); match != nil {
name := string(match["name"])
var args map[string]string
- if argsMatch := extractAllMap(reGGCodeBlockArgs, match["args"]); argsMatch != nil {
+ if argsMatch := extractAllMap(reGGCodeArgs, match["args"]); argsMatch != nil {
args = make(map[string]string)
for i := range argsMatch["arg"] {
arg := string(argsMatch["arg"][i])
@@ -90,7 +108,7 @@ func (s ggcodeParserBlock) Open(parent ast.Node, reader text.Reader, pc parser.C
}
}
-func (s ggcodeParserBlock) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
+func (s ggcodeBlockParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
line, _ := reader.PeekLine()
if string(bytes.TrimSpace(line)) == "!!!" {
reader.Advance(3)
@@ -99,16 +117,80 @@ func (s ggcodeParserBlock) Continue(node ast.Node, reader text.Reader, pc parser
return parser.Continue | parser.HasChildren
}
-func (s ggcodeParserBlock) Close(node ast.Node, reader text.Reader, pc parser.Context) {}
+func (s ggcodeBlockParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {}
-func (s ggcodeParserBlock) CanInterruptParagraph() bool {
+func (s ggcodeBlockParser) CanInterruptParagraph() bool {
return false
}
-func (s ggcodeParserBlock) CanAcceptIndentedLine() bool {
+func (s ggcodeBlockParser) CanAcceptIndentedLine() bool {
return false
}
+// Inline parser stuff
+
+type ggcodeInlineParser struct {
+ Preview bool
+}
+
+var _ parser.InlineParser = ggcodeInlineParser{}
+
+func (s ggcodeInlineParser) Trigger() []byte {
+ return []byte("!()")
+}
+
+func (s ggcodeInlineParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
+ restOfLine, segment := block.PeekLine() // Gets the rest of the line (starting at the current parser cursor index), and the segment representing the indices in the source text.
+ if match := extractMap(reGGCodeInline, restOfLine); match != nil {
+ name := string(match["name"])
+ var args map[string]string
+ if argsMatch := extractAllMap(reGGCodeArgs, match["args"]); argsMatch != nil {
+ args = make(map[string]string)
+ for i := range argsMatch["arg"] {
+ arg := string(argsMatch["arg"][i])
+ val := string(argsMatch["val"][i])
+ args[arg] = val
+ }
+ }
+
+ node := &ggcodeNode{
+ Name: name,
+ Args: args,
+ }
+ contentLength := len(match["content"])
+ if contentLength > 0 {
+ contentSegmentStart := segment.Start + len(match["all"]) - (contentLength + 1) // the 1 is for the end parenthesis
+ contentSegmentEnd := contentSegmentStart + contentLength
+ contentSegment := text.NewSegment(contentSegmentStart, contentSegmentEnd)
+ node.AppendChild(node, ast.NewTextSegment(contentSegment))
+ }
+
+ block.Advance(len(match["all"]))
+ return node
+ } else {
+ return nil
+ }
+}
+
+type ggcodeDelimiterParser struct {
+ Node *ggcodeNode // We need to pass this through 🙄
+}
+
+func (p ggcodeDelimiterParser) IsDelimiter(b byte) bool {
+ fmt.Println("delmit", string(b))
+ return b == '(' || b == ')'
+}
+
+func (p ggcodeDelimiterParser) CanOpenCloser(opener, closer *parser.Delimiter) bool {
+ fmt.Println("oopen")
+ return opener.Char == '(' && closer.Char == ')'
+}
+
+func (p ggcodeDelimiterParser) OnMatch(consumes int) gast.Node {
+ fmt.Println("out!")
+ return p.Node
+}
+
// ----------------------
// AST node
// ----------------------
@@ -180,7 +262,10 @@ type ggcodeExtension struct {
func (e ggcodeExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithBlockParsers(
- util.Prioritized(ggcodeParserBlock{Preview: e.Preview}, 500),
+ util.Prioritized(ggcodeBlockParser{Preview: e.Preview}, 500),
+ ))
+ m.Parser().AddOptions(parser.WithInlineParsers(
+ util.Prioritized(ggcodeInlineParser{Preview: e.Preview}, 500),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(newGGCodeHTMLRenderer(), 500),