Skip to content

Commit

Permalink
more documentation, parse bits, expand repl
Browse files Browse the repository at this point in the history
  • Loading branch information
HannesKimara committed Oct 29, 2023
1 parent 2207273 commit 6f11ea9
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 4 deletions.
107 changes: 107 additions & 0 deletions ast/astutils/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package astutils

import (
"fmt"
"strings"

"github.com/HannesKimara/cddlc/ast"
"github.com/HannesKimara/cddlc/token"
)

const spaceChar = "\t"

type printer struct {
level uint
builder *strings.Builder
}

func (p *printer) format(node ast.Node) string {
var s string

p.level += 1
switch val := node.(type) {
case *ast.Identifier:
s = p.formatIdent(val)
case *ast.TypeChoice:
s = p.formatTypeChoice(val)
case *ast.IntegerLiteral:
s = p.formatIntegerLiteral(val)
case *ast.FloatLiteral:
s = p.formatFloatLiteral(val)
case *ast.IntegerType:
s = p.formatIntType(val)
case *ast.FloatType:
s = p.formatFloatType(val)
case *ast.Enumeration:
s = p.formatEnumeration(val)
case *ast.Group:
s = p.formatGroup(val)
default:
s = fmt.Sprintf("%T", val)
}
p.level -= 1

return s
}

func (p *printer) formatIdent(ident *ast.Identifier) string {
return fmt.Sprintf("Ident ( Name: '%s' ) %s", ident.Name, p.formatPosition(ident.Pos))
}

func (p *printer) formatTypeChoice(ch *ast.TypeChoice) string {
pre := p.spaceLevel(spaceChar)
return fmt.Sprintf("TypeChoice %s (\n%sFirst : %s, \n%sSecond : %s\n)", p.formatPosition(ch.Pos), pre, p.format(ch.First), pre, p.format(ch.Second))
}

func (p *printer) formatIntegerLiteral(il *ast.IntegerLiteral) string {
return fmt.Sprintf("IntegerLiteral (Literal: '%d') %s", il.Literal, p.formatPosition(il.Pos))
}

func (p *printer) formatFloatLiteral(fl *ast.FloatLiteral) string {
return fmt.Sprintf("FloatLiteral ( Literal: '%f' ) %s", fl.Literal, p.formatPosition(fl.Start()))
}

func (p *printer) formatIntType(it *ast.IntegerType) string {
return fmt.Sprintf("IntegerType ( Token: '%s' ) %s", it.Token, p.formatPosition(it.Start()))
}

func (p *printer) formatFloatType(ft *ast.FloatType) string {
return fmt.Sprintf("FloatType ( Token: '%s' ) %s", ft.Token, p.formatPosition(ft.Start()))
}

func (p *printer) formatEnumeration(en *ast.Enumeration) string {
pre := p.spaceLevel(spaceChar)
return fmt.Sprintf("Enumeration %s (\n%sValue: %s\n%s)", p.formatPosition(en.Pos), pre, p.format(en.Value), pre)
}

func (p *printer) formatGroup(gl *ast.Group) string {
pre := p.spaceLevel(spaceChar)
epre := pre + spaceChar
return fmt.Sprintf("Group %s (\n%sEntries: %d\n%s)", p.formatPosition(gl.Pos), epre, len(gl.Entries), pre)
}

func (p *printer) formatPosition(pos token.Position) string {
return fmt.Sprintf("{ Column: %d, Line: %d }", pos.Column, pos.Line)
}

func (p *printer) spaceLevel(space string) string {
return strings.Repeat(space, int(p.level))
}

func Print(node ast.Node) {
p := printer{
level: 0,
builder: &strings.Builder{},
}

fmt.Println(p.format(node))
}

func Format(node ast.Node) string {
p := printer{
level: 0,
builder: &strings.Builder{},
}

return p.format(node)
}
26 changes: 26 additions & 0 deletions ast/control_bits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ast

import "github.com/HannesKimara/cddlc/token"

// Bits represents the AST Node for `.bits` control operator
type Bits struct {
// Pos: the position of the .size token
Pos token.Position

// Token: the token responsible for the node
Token token.Token

// Base: the base node
Base Node

// Constraint: the bits constraint to apply
Contstraint Node
}

func (r *Bits) Start() token.Position {
return r.Base.Start()
}

func (r *Bits) End() token.Position {
return r.Contstraint.End()
}
41 changes: 40 additions & 1 deletion cmd/cddlc/commands/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"errors"
"fmt"
"os"
"strings"

"github.com/HannesKimara/cddlc/ast"
"github.com/HannesKimara/cddlc/ast/astutils"
"github.com/HannesKimara/cddlc/lexer"
"github.com/HannesKimara/cddlc/parser"
"github.com/HannesKimara/cddlc/token"
Expand All @@ -19,6 +21,7 @@ const PROMPT string = ">>>"

func Repl(cCtx *cli.Context) error {
scanner := bufio.NewScanner(os.Stdin)
parseVerbose := false

fmt.Println("Welcome to the cddlc quick repl.")
environ := env.NewEnvironment()
Expand All @@ -31,6 +34,40 @@ func Repl(cCtx *cli.Context) error {
return errors.New("could not scan")
}

text := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(text, ":help") {
fmt.Println(":help - Prints this help message")
fmt.Println(":tree - Prints the syntax tree for a value in scope")
fmt.Println()
fmt.Println(":pv - Toggle printing the type after every execution")
fmt.Println()
fmt.Println(":exit - Exits the REPL")
continue
}

if strings.HasPrefix(text, ":pv") {
parseVerbose = !parseVerbose
continue
}

if strings.HasPrefix(text, ":exit") {
fmt.Println("exiting...")
os.Exit(0)
}

if strings.HasPrefix(text, ":tree") {
args := strings.Split(text, " ")[1:]
for _, arg := range args {
val := environ.Get(arg)
if val != nil {
fmt.Printf(arg + ": ")
astutils.Print(val)
}
}

continue
}

if cCtx.Bool("lex") {
printTokens(scanner.Bytes())
}
Expand All @@ -45,7 +82,9 @@ func Repl(cCtx *cli.Context) error {
fmt.Println(errs.String())
}

printcddlShort(cddl)
if parseVerbose {
printcddlShort(cddl)
}

}
}
Expand Down
31 changes: 31 additions & 0 deletions examples/lexer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"fmt"

"github.com/HannesKimara/cddlc/lexer"
"github.com/HannesKimara/cddlc/token"
)

func main() {
src := `
min-age = 18
max-age = 150
byte = uint .size 1
public-key = [24*24 byte]
person = (name: tstr, public-key: public-key)
adult = (person, age: min-age .. max-age) ; adults are composed from person
`

lex := lexer.NewLexer([]byte(src))

for {
tok, pos, lit := lex.Scan()
fmt.Printf("%s: %s -> %s\n", pos, tok, lit)
if tok == token.EOF {
break
}
}
}
32 changes: 32 additions & 0 deletions examples/parser/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"fmt"
"log"

"github.com/HannesKimara/cddlc/lexer"
"github.com/HannesKimara/cddlc/parser"
)

func main() {
src := `
min-age = 18
max-age = 150
byte = uint .size 1
public-key = [24*24 byte]
person = (name: tstr, public-key: public-key)
adult = (~person, age: min-age .. max-age) ; adults are composed from person
`

lex := lexer.NewLexer([]byte(src))
p := parser.NewParser(lex)
cddl, err := p.ParseFile()

if err != nil {
log.Fatal()
}

fmt.Printf("Found %d rules\n", cddl.Rules)
}
46 changes: 46 additions & 0 deletions lexer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Package lexer

The lexer package produces a set of valid tokens from a valid cddl source.

## Example

This example covers tokeizing a cddl source with literals, control operators, ranges and compositions. For more examples go to the [examples](../examples/) folder.

```go
package main

import (
"fmt"

"github.com/HannesKimara/cddlc/lexer"
"github.com/HannesKimara/cddlc/token"
)

func main() {
src := `
min-age = 18
max-age = 150
byte = uint .size 1
public-key = [24*24 byte]
person = (name: tstr, public-key: public-key)
adult = (person, age: min-age .. max-age) ; adults are composed from person
`

lex := lexer.NewLexer([]byte(src))

for {
tok, pos, lit := lex.Scan()
fmt.Printf("%s: %s -> %s\n", pos, tok, lit)
if tok == token.EOF {
break
}
}
}

```

## License

This project is licensed under the Apache-2.0 license. Please see the [LICENSE](../LICENSE) file for more details.
46 changes: 46 additions & 0 deletions parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Package parser

The parser package builds an abstract syntax tree from a set of source tokens.

## Example

This example covers parsing a cddl source with literals, control operators, ranges and compositions. For more examples go to the [examples](../examples/) folder.

```go
package main

import (
"fmt"
"log"

"github.com/HannesKimara/cddlc/lexer"
"github.com/HannesKimara/cddlc/parser"
)

func main() {
src := `
min-age = 18
max-age = 150
byte = uint .size 1
public-key = [24*24 byte]
person = (name: tstr, public-key: public-key)
adult = (~person, age: min-age .. max-age) ; adults are composed from person
`

lex := lexer.NewLexer([]byte(src))
p := parser.NewParser(lex)
cddl, err := p.ParseFile()

if err != nil {
log.Fatal()
}

fmt.Printf("Found %d rules\n", cddl.Rules)
}
```

## License

This project is licensed under the Apache-2.0 license. Please see the [LICENSE](../LICENSE) file for more details.
Loading

0 comments on commit 6f11ea9

Please sign in to comment.