Reduced token size

This commit is contained in:
2024-07-21 14:35:06 +02:00
parent ca36d34cb9
commit 04ba68a075
47 changed files with 543 additions and 764 deletions

View File

@ -1,6 +1,4 @@
package ast
import "fmt"
type Node fmt.Stringer
type Node any
type AST []Node

View File

@ -8,7 +8,3 @@ import (
type Assign struct {
Expression *expression.Expression
}
func (node *Assign) String() string {
return node.Expression.String()
}

View File

@ -6,7 +6,3 @@ import "git.akyoto.dev/cli/q/src/build/expression"
type Call struct {
Expression *expression.Expression
}
func (node *Call) String() string {
return node.Expression.String()
}

View File

@ -3,31 +3,31 @@ package ast
import "git.akyoto.dev/cli/q/src/build/token"
// Count counts how often the given token appears in the AST.
func Count(body AST, kind token.Kind, name string) int {
func Count(body AST, buffer []byte, kind token.Kind, name string) int {
count := 0
for _, node := range body {
switch node := node.(type) {
case *Assign:
count += node.Expression.Count(kind, name)
count += node.Expression.Count(buffer, kind, name)
case *Call:
count += node.Expression.Count(kind, name)
count += node.Expression.Count(buffer, kind, name)
case *Define:
count += node.Value.Count(kind, name)
count += node.Value.Count(buffer, kind, name)
case *Return:
if node.Value != nil {
count += node.Value.Count(kind, name)
count += node.Value.Count(buffer, kind, name)
}
case *If:
count += node.Condition.Count(kind, name)
count += Count(node.Body, kind, name)
count += node.Condition.Count(buffer, kind, name)
count += Count(node.Body, buffer, kind, name)
case *Loop:
count += Count(node.Body, kind, name)
count += Count(node.Body, buffer, kind, name)
default:
panic("unknown AST type")

View File

@ -1,8 +1,6 @@
package ast
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/token"
)
@ -12,7 +10,3 @@ type Define struct {
Value *expression.Expression
Name token.Token
}
func (node *Define) String() string {
return fmt.Sprintf("(= %s %s)", node.Name.Text(), node.Value)
}

View File

@ -1,8 +1,6 @@
package ast
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/expression"
)
@ -11,7 +9,3 @@ type If struct {
Condition *expression.Expression
Body AST
}
func (node *If) String() string {
return fmt.Sprintf("(if %s %s)", node.Condition, node.Body)
}

View File

@ -1,12 +1,6 @@
package ast
import "fmt"
// Loop represents a block of repeatable statements.
type Loop struct {
Body AST
}
func (node *Loop) String() string {
return fmt.Sprintf("(loop %s)", node.Body)
}

View File

@ -3,16 +3,15 @@ package ast
import (
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/expression"
"git.akyoto.dev/cli/q/src/build/keyword"
"git.akyoto.dev/cli/q/src/build/token"
)
// Parse generates an AST from a list of tokens.
func Parse(tokens token.List) (AST, error) {
func Parse(tokens []token.Token, buffer []byte) (AST, error) {
tree := make(AST, 0, len(tokens)/64)
err := EachInstruction(tokens, func(instruction token.List) error {
node, err := toASTNode(instruction)
node, err := toASTNode(instruction, buffer)
if err == nil && node != nil {
tree = append(tree, node)
@ -25,11 +24,9 @@ func Parse(tokens token.List) (AST, error) {
}
// toASTNode generates an AST node from an instruction.
func toASTNode(tokens token.List) (Node, error) {
if tokens[0].Kind == token.Keyword {
word := tokens[0].Text()
if word == keyword.Return {
func toASTNode(tokens token.List, buffer []byte) (Node, error) {
if tokens[0].IsKeyword() {
if tokens[0].Kind == token.Return {
if len(tokens) == 1 {
return &Return{}, nil
}
@ -38,7 +35,7 @@ func toASTNode(tokens token.List) (Node, error) {
return &Return{Value: value}, nil
}
if keywordHasBlock(word) {
if keywordHasBlock(tokens[0].Kind) {
blockStart := tokens.IndexKind(token.BlockStart)
blockEnd := tokens.LastIndexKind(token.BlockEnd)
@ -50,19 +47,19 @@ func toASTNode(tokens token.List) (Node, error) {
return nil, errors.New(errors.MissingBlockEnd, nil, tokens[len(tokens)-1].End())
}
body, err := Parse(tokens[blockStart+1 : blockEnd])
body, err := Parse(tokens[blockStart+1:blockEnd], buffer)
switch word {
case keyword.If:
switch tokens[0].Kind {
case token.If:
condition := expression.Parse(tokens[1:blockStart])
return &If{Condition: condition, Body: body}, err
case keyword.Loop:
case token.Loop:
return &Loop{Body: body}, err
}
}
return nil, errors.New(&errors.KeywordNotImplemented{Keyword: word}, nil, tokens[0].Position)
return nil, errors.New(&errors.KeywordNotImplemented{Keyword: tokens[0].Text(buffer)}, nil, tokens[0].Position)
}
expr := expression.Parse(tokens)
@ -92,26 +89,26 @@ func toASTNode(tokens token.List) (Node, error) {
return &Call{Expression: expr}, nil
default:
return nil, errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, nil, expr.Token.Position)
return nil, errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text(buffer)}, nil, expr.Token.Position)
}
}
// IsAssignment returns true if the expression is an assignment.
func IsAssignment(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Bytes[len(expr.Token.Bytes)-1] == '='
return expr.Token.IsAssignment()
}
// IsFunctionCall returns true if the expression is a function call.
func IsFunctionCall(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Text() == "λ"
return expr.Token.Kind == token.Call
}
// IsVariableDefinition returns true if the expression is a variable definition.
func IsVariableDefinition(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Text() == ":="
return expr.Token.Kind == token.Define
}
// keywordHasBlock returns true if the keyword requires a block.
func keywordHasBlock(word string) bool {
return word == keyword.If || word == keyword.Loop
func keywordHasBlock(kind token.Kind) bool {
return kind == token.If || kind == token.Loop
}

View File

@ -1,8 +1,6 @@
package ast
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/expression"
)
@ -10,7 +8,3 @@ import (
type Return struct {
Value *expression.Expression
}
func (node *Return) String() string {
return fmt.Sprintf("(return %s)", node.Value)
}