Skip to content

Commit 30b0f90

Browse files
committed
feat: implement inline & block code
this commit introduces correct parsing of inline code and block code, it also fixes an issue with heading parsing, adds support for toc generation as well as writing the html output to a file. - --toc flag enables table of content generation via the parser.Parser.GenerateToc function - add headings field to parser.Parser - replaced some spagetti - moved info logs with time tracking to main - new info log when full execution is done - write to file
1 parent a7c0e70 commit 30b0f90

File tree

2 files changed

+118
-10
lines changed

2 files changed

+118
-10
lines changed

fleck.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package main
22

33
import (
4+
"bufio"
45
"fmt"
56
"os"
7+
"strings"
8+
"time"
69

710
"github.com/xnacly/fleck/cli"
811
"github.com/xnacly/fleck/logger"
@@ -12,6 +15,7 @@ import (
1215
)
1316

1417
func main() {
18+
start := time.Now()
1519
cli.ARGUMENTS = cli.ParseCli()
1620
if len(cli.ARGUMENTS.InputFile) == 0 {
1721
cli.PrintShortHelp()
@@ -30,14 +34,38 @@ func main() {
3034
fileName = fileName + ".fleck"
3135
}
3236

37+
lexerStart := time.Now()
3338
s := scanner.New(fileName)
3439
tokens := s.Lex()
40+
logger.LInfo("lexed " + fmt.Sprint(len(tokens)) + " token, took " + time.Since(lexerStart).String())
41+
42+
parserStart := time.Now()
3543
p := parser.New(tokens)
36-
fmt.Println(p.Parse())
44+
result := p.Parse()
45+
logger.LInfo("parsed " + fmt.Sprint(len(result)) + " items, took " + time.Since(parserStart).String())
46+
47+
writeStart := time.Now()
48+
name := strings.Split(fileName, ".")[0] + ".html"
49+
out, err := os.Create(name)
50+
writer := bufio.NewWriter(out)
51+
52+
if err != nil {
53+
logger.LError("failed to open file: " + err.Error())
54+
}
55+
56+
if cli.GetFlag(cli.ARGUMENTS, "toc") {
57+
writer.WriteString(p.GenerateToc())
58+
}
59+
60+
for _, e := range result {
61+
writer.WriteString(e.String() + "\n")
62+
}
63+
64+
writer.Flush()
65+
logger.LInfo("wrote generated html to '" + name + "', took: " + time.Since(writeStart).String())
3766

3867
defer func() {
3968
if cli.GetFlag(cli.ARGUMENTS, "preprocessor-enabled") {
40-
4169
if cli.GetFlag(cli.ARGUMENTS, "keep-temp") {
4270
return
4371
}
@@ -47,5 +75,6 @@ func main() {
4775
logger.LWarn(err.Error())
4876
}
4977
}
78+
logger.LInfo("did everything, took: " + time.Since(start).String())
5079
}()
5180
}

parser/parser.go

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import (
44
"fmt"
55
"strings"
66

7+
"github.com/xnacly/fleck/cli"
78
"github.com/xnacly/fleck/logger"
89
"github.com/xnacly/fleck/scanner"
910
)
1011

1112
type Parser struct {
12-
tokens []scanner.Token
13-
tags []Tag
14-
current int
13+
tokens []scanner.Token
14+
tags []Tag
15+
current int
16+
headings []Heading
1517
}
1618

1719
func New(tokens []scanner.Token) Parser {
@@ -32,8 +34,9 @@ func (p *Parser) Parse() []Tag {
3234
}
3335

3436
func (p *Parser) tag() Tag {
35-
// parse headings just before paragraphs at the end, everything else before
36-
if p.peek().Kind == scanner.HASH && (p.prev().Kind == scanner.NEWLINE || p.prev().Kind == 0) {
37+
if p.check(scanner.BACKTICK) {
38+
return p.code()
39+
} else if p.check(scanner.HASH) && (p.prev().Kind == scanner.NEWLINE || p.prev().Kind == 0) {
3740
return p.heading()
3841
} else {
3942
// TODO: currently skips everything except headings, fix that, this here is a temp messuare to keep the program from endless looping
@@ -42,15 +45,63 @@ func (p *Parser) tag() Tag {
4245
}
4346
}
4447

48+
func (p *Parser) code() Tag {
49+
p.advance()
50+
if p.check(scanner.TEXT) {
51+
// skip the `
52+
p.advance()
53+
if p.check(scanner.BACKTICK) {
54+
return CodeInline{
55+
text: p.prev().Value,
56+
}
57+
}
58+
} else if p.check(scanner.BACKTICK) {
59+
p.advance()
60+
if !p.check(scanner.BACKTICK) {
61+
return CodeInline{
62+
text: "",
63+
}
64+
}
65+
p.advance()
66+
language := p.peek().Value
67+
// skip lang definition
68+
p.advance()
69+
// skip newline
70+
p.advance()
71+
72+
b := strings.Builder{}
73+
for !p.check(scanner.BACKTICK) {
74+
if p.check(scanner.TEXT) {
75+
b.WriteString(p.peek().Value)
76+
} else {
77+
b.WriteRune(scanner.TOKEN_SYMBOL_MAP[p.peek().Kind])
78+
}
79+
p.advance()
80+
}
81+
82+
// skip the three ```
83+
p.advance()
84+
p.advance()
85+
p.advance()
86+
87+
return CodeBlock{
88+
language: language,
89+
text: b.String(),
90+
}
91+
}
92+
return nil
93+
}
94+
4595
func (p *Parser) paragraph() Tag {
4696
return Paragraph{}
4797
}
4898

99+
// TODO: find a way to parse the rest of the tokens as well, not just write them to the heading
49100
func (p *Parser) heading() Tag {
50101
var lvl uint = 0
51102
children := make([]scanner.Token, 0)
52103

53-
for p.peek().Kind == scanner.HASH {
104+
for p.check(scanner.HASH) {
54105
lvl++
55106
p.advance()
56107
}
@@ -60,6 +111,7 @@ func (p *Parser) heading() Tag {
60111
p.advance()
61112
}
62113

114+
// skip the newline
63115
p.advance()
64116

65117
b := strings.Builder{}
@@ -74,11 +126,38 @@ func (p *Parser) heading() Tag {
74126
b.WriteRune(scanner.TOKEN_SYMBOL_MAP[c.Kind])
75127
}
76128
}
77-
78-
return Heading{
129+
heading := Heading{
79130
lvl: lvl,
80131
text: b.String(),
81132
}
133+
134+
if cli.GetFlag(cli.ARGUMENTS, "toc") {
135+
p.headings = append(p.headings, heading)
136+
}
137+
return heading
138+
}
139+
140+
func (p *Parser) GenerateToc() string {
141+
headingMap := map[uint]uint{
142+
1: 0,
143+
2: 0,
144+
3: 0,
145+
}
146+
147+
b := strings.Builder{}
148+
b.WriteString("<h3>Table of contents</h3>")
149+
b.WriteString("<ul>")
150+
for _, v := range p.headings {
151+
// TODO: make this a -toc-level=x flag
152+
// TODO: switch over levels, indent subheadings using <ul> in <ul>
153+
if v.lvl > 3 {
154+
continue
155+
}
156+
headingMap[v.lvl]++
157+
b.WriteString(fmt.Sprintf("<li><a href=\"#%s\">%d.%d.%d</a>: %s</li>", v.text, headingMap[1], headingMap[2], headingMap[3], v.text))
158+
}
159+
b.WriteString("</ul>")
160+
return b.String()
82161
}
83162

84163
func (p *Parser) match(types ...uint) bool {

0 commit comments

Comments
 (0)