Skip to content

Commit

Permalink
feat: implement inline & block code
Browse files Browse the repository at this point in the history
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
  • Loading branch information
xNaCly committed Apr 11, 2023
1 parent a7c0e70 commit 30b0f90
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 10 deletions.
33 changes: 31 additions & 2 deletions fleck.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package main

import (
"bufio"
"fmt"
"os"
"strings"
"time"

"github.com/xnacly/fleck/cli"
"github.com/xnacly/fleck/logger"
Expand All @@ -12,6 +15,7 @@ import (
)

func main() {
start := time.Now()
cli.ARGUMENTS = cli.ParseCli()
if len(cli.ARGUMENTS.InputFile) == 0 {
cli.PrintShortHelp()
Expand All @@ -30,14 +34,38 @@ func main() {
fileName = fileName + ".fleck"
}

lexerStart := time.Now()
s := scanner.New(fileName)
tokens := s.Lex()
logger.LInfo("lexed " + fmt.Sprint(len(tokens)) + " token, took " + time.Since(lexerStart).String())

parserStart := time.Now()
p := parser.New(tokens)
fmt.Println(p.Parse())
result := p.Parse()
logger.LInfo("parsed " + fmt.Sprint(len(result)) + " items, took " + time.Since(parserStart).String())

writeStart := time.Now()
name := strings.Split(fileName, ".")[0] + ".html"
out, err := os.Create(name)
writer := bufio.NewWriter(out)

if err != nil {
logger.LError("failed to open file: " + err.Error())
}

if cli.GetFlag(cli.ARGUMENTS, "toc") {
writer.WriteString(p.GenerateToc())
}

for _, e := range result {
writer.WriteString(e.String() + "\n")
}

writer.Flush()
logger.LInfo("wrote generated html to '" + name + "', took: " + time.Since(writeStart).String())

defer func() {
if cli.GetFlag(cli.ARGUMENTS, "preprocessor-enabled") {

if cli.GetFlag(cli.ARGUMENTS, "keep-temp") {
return
}
Expand All @@ -47,5 +75,6 @@ func main() {
logger.LWarn(err.Error())
}
}
logger.LInfo("did everything, took: " + time.Since(start).String())
}()
}
95 changes: 87 additions & 8 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import (
"fmt"
"strings"

"github.com/xnacly/fleck/cli"
"github.com/xnacly/fleck/logger"
"github.com/xnacly/fleck/scanner"
)

type Parser struct {
tokens []scanner.Token
tags []Tag
current int
tokens []scanner.Token
tags []Tag
current int
headings []Heading
}

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

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

func (p *Parser) code() Tag {
p.advance()
if p.check(scanner.TEXT) {
// skip the `
p.advance()
if p.check(scanner.BACKTICK) {
return CodeInline{
text: p.prev().Value,
}
}
} else if p.check(scanner.BACKTICK) {
p.advance()
if !p.check(scanner.BACKTICK) {
return CodeInline{
text: "",
}
}
p.advance()
language := p.peek().Value
// skip lang definition
p.advance()
// skip newline
p.advance()

b := strings.Builder{}
for !p.check(scanner.BACKTICK) {
if p.check(scanner.TEXT) {
b.WriteString(p.peek().Value)
} else {
b.WriteRune(scanner.TOKEN_SYMBOL_MAP[p.peek().Kind])
}
p.advance()
}

// skip the three ```
p.advance()
p.advance()
p.advance()

return CodeBlock{
language: language,
text: b.String(),
}
}
return nil
}

func (p *Parser) paragraph() Tag {
return Paragraph{}
}

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

for p.peek().Kind == scanner.HASH {
for p.check(scanner.HASH) {
lvl++
p.advance()
}
Expand All @@ -60,6 +111,7 @@ func (p *Parser) heading() Tag {
p.advance()
}

// skip the newline
p.advance()

b := strings.Builder{}
Expand All @@ -74,11 +126,38 @@ func (p *Parser) heading() Tag {
b.WriteRune(scanner.TOKEN_SYMBOL_MAP[c.Kind])
}
}

return Heading{
heading := Heading{
lvl: lvl,
text: b.String(),
}

if cli.GetFlag(cli.ARGUMENTS, "toc") {
p.headings = append(p.headings, heading)
}
return heading
}

func (p *Parser) GenerateToc() string {
headingMap := map[uint]uint{
1: 0,
2: 0,
3: 0,
}

b := strings.Builder{}
b.WriteString("<h3>Table of contents</h3>")
b.WriteString("<ul>")
for _, v := range p.headings {
// TODO: make this a -toc-level=x flag
// TODO: switch over levels, indent subheadings using <ul> in <ul>
if v.lvl > 3 {
continue
}
headingMap[v.lvl]++
b.WriteString(fmt.Sprintf("<li><a href=\"#%s\">%d.%d.%d</a>: %s</li>", v.text, headingMap[1], headingMap[2], headingMap[3], v.text))
}
b.WriteString("</ul>")
return b.String()
}

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

0 comments on commit 30b0f90

Please sign in to comment.