Simplified ast package
This commit is contained in:
parent
45a36a645a
commit
b655950516
@ -1,7 +1,63 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/expression"
|
||||||
|
|
||||||
// Node is an interface used for all types of AST nodes.
|
// Node is an interface used for all types of AST nodes.
|
||||||
type Node any
|
type Node any
|
||||||
|
|
||||||
// AST is an abstract syntax tree which is simply a list of nodes.
|
// AST is an abstract syntax tree which is simply a list of nodes.
|
||||||
type AST []Node
|
type AST []Node
|
||||||
|
|
||||||
|
// Assert is a condition that must be true, otherwise the program stops.
|
||||||
|
type Assert struct {
|
||||||
|
Condition *expression.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign is an assignment to an existing variable or memory location.
|
||||||
|
type Assign struct {
|
||||||
|
Expression *expression.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call is a function call.
|
||||||
|
type Call struct {
|
||||||
|
Expression *expression.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case is a case inside a switch.
|
||||||
|
type Case struct {
|
||||||
|
Condition *expression.Expression
|
||||||
|
Body AST
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define is a variable definition.
|
||||||
|
type Define struct {
|
||||||
|
Expression *expression.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// For is a loop with a defined iteration limit.
|
||||||
|
type For struct {
|
||||||
|
Head *expression.Expression
|
||||||
|
Body AST
|
||||||
|
}
|
||||||
|
|
||||||
|
// If is a conditional branch.
|
||||||
|
type If struct {
|
||||||
|
Condition *expression.Expression
|
||||||
|
Body AST
|
||||||
|
Else AST
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop is an infinite loop.
|
||||||
|
type Loop struct {
|
||||||
|
Body AST
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return is a return statement.
|
||||||
|
type Return struct {
|
||||||
|
Values []*expression.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch is a conditional branch with multiple cases.
|
||||||
|
type Switch struct {
|
||||||
|
Cases []Case
|
||||||
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Assert represents a condition that must be true, otherwise the program stops.
|
|
||||||
type Assert struct {
|
|
||||||
Condition *expression.Expression
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Assign represents an assignment to an existing variable or memory location.
|
|
||||||
type Assign struct {
|
|
||||||
Expression *expression.Expression
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/expression"
|
|
||||||
|
|
||||||
// Call represents a function call.
|
|
||||||
type Call struct {
|
|
||||||
Expression *expression.Expression
|
|
||||||
}
|
|
@ -25,6 +25,10 @@ func Count(body AST, buffer []byte, kind token.Kind, name string) uint8 {
|
|||||||
count += Count(node.Body, buffer, kind, name)
|
count += Count(node.Body, buffer, kind, name)
|
||||||
count += Count(node.Else, buffer, kind, name)
|
count += Count(node.Else, buffer, kind, name)
|
||||||
|
|
||||||
|
case *For:
|
||||||
|
count += node.Head.Count(buffer, kind, name)
|
||||||
|
count += Count(node.Body, buffer, kind, name)
|
||||||
|
|
||||||
case *Loop:
|
case *Loop:
|
||||||
count += Count(node.Body, buffer, kind, name)
|
count += Count(node.Body, buffer, kind, name)
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define represents a variable definition.
|
|
||||||
type Define struct {
|
|
||||||
Expression *expression.Expression
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/expression"
|
|
||||||
|
|
||||||
// For is a loop with a defined iteration limit.
|
|
||||||
type For struct {
|
|
||||||
Head *expression.Expression
|
|
||||||
Body AST
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
|
||||||
)
|
|
||||||
|
|
||||||
// If represents an if statement.
|
|
||||||
type If struct {
|
|
||||||
Condition *expression.Expression
|
|
||||||
Body AST
|
|
||||||
Else AST
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
// Loop is a block of infinitely repeating instructions.
|
|
||||||
type Loop struct {
|
|
||||||
Body AST
|
|
||||||
}
|
|
@ -1,18 +1,18 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
"git.akyoto.dev/cli/q/src/token"
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse generates an AST from a list of tokens.
|
// Parse generates an AST from a list of tokens.
|
||||||
func Parse(tokens []token.Token, source []byte) (AST, error) {
|
func Parse(tokens []token.Token, file *fs.File) (AST, error) {
|
||||||
nodes := make(AST, 0, len(tokens)/64)
|
nodes := make(AST, 0, len(tokens)/64)
|
||||||
|
|
||||||
err := EachInstruction(tokens, func(instruction token.List) error {
|
err := eachInstruction(tokens, func(instruction token.List) error {
|
||||||
node, err := parseNode(instruction, source, nodes)
|
node, err := parseInstruction(instruction, file, nodes)
|
||||||
|
|
||||||
if err == nil && node != nil {
|
if node != nil {
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,18 +21,3 @@ func Parse(tokens []token.Token, source []byte) (AST, error) {
|
|||||||
|
|
||||||
return nodes, err
|
return nodes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAssignment returns true if the expression is an assignment.
|
|
||||||
func IsAssignment(expr *expression.Expression) bool {
|
|
||||||
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.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsVariableDefinition returns true if the expression is a variable definition.
|
|
||||||
func IsVariableDefinition(expr *expression.Expression) bool {
|
|
||||||
return expr.Token.Kind == token.Define
|
|
||||||
}
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return represents a return statement.
|
|
||||||
type Return struct {
|
|
||||||
Values []*expression.Expression
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Switch represents a switch statement.
|
|
||||||
type Switch struct {
|
|
||||||
Cases []Case
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case represents a case inside a switch.
|
|
||||||
type Case struct {
|
|
||||||
Condition *expression.Expression
|
|
||||||
Body AST
|
|
||||||
}
|
|
26
src/ast/block.go
Normal file
26
src/ast/block.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// block retrieves the start and end position of a block.
|
||||||
|
func block(tokens token.List, file *fs.File) (blockStart int, blockEnd int, body AST, err error) {
|
||||||
|
blockStart = tokens.IndexKind(token.BlockStart)
|
||||||
|
blockEnd = tokens.LastIndexKind(token.BlockEnd)
|
||||||
|
|
||||||
|
if blockStart == -1 {
|
||||||
|
err = errors.New(errors.MissingBlockStart, file, tokens[0].End())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockEnd == -1 {
|
||||||
|
err = errors.New(errors.MissingBlockEnd, file, tokens[len(tokens)-1].End())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err = Parse(tokens[blockStart+1:blockEnd], file)
|
||||||
|
return
|
||||||
|
}
|
@ -2,13 +2,13 @@ package ast
|
|||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/token"
|
import "git.akyoto.dev/cli/q/src/token"
|
||||||
|
|
||||||
// EachInstruction calls the function on each instruction.
|
// eachInstruction calls the function on each AST node.
|
||||||
func EachInstruction(body token.List, call func(token.List) error) error {
|
func eachInstruction(tokens token.List, call func(token.List) error) error {
|
||||||
start := 0
|
start := 0
|
||||||
groupLevel := 0
|
groupLevel := 0
|
||||||
blockLevel := 0
|
blockLevel := 0
|
||||||
|
|
||||||
for i, t := range body {
|
for i, t := range tokens {
|
||||||
if start == i && t.Kind == token.NewLine {
|
if start == i && t.Kind == token.NewLine {
|
||||||
start = i + 1
|
start = i + 1
|
||||||
continue
|
continue
|
||||||
@ -20,7 +20,7 @@ func EachInstruction(body token.List, call func(token.List) error) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := call(body[start:i])
|
err := call(tokens[start:i])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -44,7 +44,7 @@ func EachInstruction(body token.List, call func(token.List) error) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := call(body[start : i+1])
|
err := call(tokens[start : i+1])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -54,8 +54,8 @@ func EachInstruction(body token.List, call func(token.List) error) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if start != len(body) {
|
if start != len(tokens) {
|
||||||
return call(body[start:])
|
return call(tokens[start:])
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
21
src/ast/helpers.go
Normal file
21
src/ast/helpers.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsAssignment returns true if the expression is an assignment.
|
||||||
|
func IsAssignment(expr *expression.Expression) bool {
|
||||||
|
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.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsVariableDefinition returns true if the expression is a variable definition.
|
||||||
|
func IsVariableDefinition(expr *expression.Expression) bool {
|
||||||
|
return expr.Token.Kind == token.Define
|
||||||
|
}
|
@ -2,15 +2,16 @@ package ast
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
"git.akyoto.dev/cli/q/src/token"
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseSwitch generates the cases inside a switch statement.
|
// parseCases generates the cases inside a switch statement.
|
||||||
func parseSwitch(tokens token.List, source []byte) ([]Case, error) {
|
func parseCases(tokens token.List, file *fs.File) ([]Case, error) {
|
||||||
var cases []Case
|
var cases []Case
|
||||||
|
|
||||||
err := EachInstruction(tokens, func(caseTokens token.List) error {
|
err := eachInstruction(tokens, func(caseTokens token.List) error {
|
||||||
blockStart, _, body, err := block(caseTokens, source)
|
blockStart, _, body, err := block(caseTokens, file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -19,7 +20,7 @@ func parseSwitch(tokens token.List, source []byte) ([]Case, error) {
|
|||||||
conditionTokens := caseTokens[:blockStart]
|
conditionTokens := caseTokens[:blockStart]
|
||||||
var condition *expression.Expression
|
var condition *expression.Expression
|
||||||
|
|
||||||
if len(conditionTokens) == 1 && conditionTokens[0].Kind == token.Identifier && conditionTokens[0].Text(source) == "_" {
|
if len(conditionTokens) == 1 && conditionTokens[0].Kind == token.Identifier && conditionTokens[0].Text(file.Bytes) == "_" {
|
||||||
condition = nil
|
condition = nil
|
||||||
} else {
|
} else {
|
||||||
condition = expression.Parse(conditionTokens)
|
condition = expression.Parse(conditionTokens)
|
@ -3,13 +3,14 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
"git.akyoto.dev/cli/q/src/token"
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseNode generates an AST node from an instruction.
|
// parseInstruction generates an AST node from an instruction.
|
||||||
func parseNode(tokens token.List, source []byte, nodes AST) (Node, error) {
|
func parseInstruction(tokens token.List, file *fs.File, nodes AST) (Node, error) {
|
||||||
if tokens[0].IsKeyword() {
|
if tokens[0].IsKeyword() {
|
||||||
return parseKeyword(tokens, source, nodes)
|
return parseKeyword(tokens, file, nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
expr := expression.Parse(tokens)
|
expr := expression.Parse(tokens)
|
||||||
@ -19,24 +20,24 @@ func parseNode(tokens token.List, source []byte, nodes AST) (Node, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case IsFunctionCall(expr):
|
||||||
|
return &Call{Expression: expr}, nil
|
||||||
|
|
||||||
case IsVariableDefinition(expr):
|
case IsVariableDefinition(expr):
|
||||||
if len(expr.Children) < 2 {
|
if len(expr.Children) < 2 {
|
||||||
return nil, errors.New(errors.MissingOperand, nil, expr.Token.End())
|
return nil, errors.New(errors.MissingOperand, file, expr.Token.End())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Define{Expression: expr}, nil
|
return &Define{Expression: expr}, nil
|
||||||
|
|
||||||
case IsAssignment(expr):
|
case IsAssignment(expr):
|
||||||
if len(expr.Children) < 2 {
|
if len(expr.Children) < 2 {
|
||||||
return nil, errors.New(errors.MissingOperand, nil, expr.Token.End())
|
return nil, errors.New(errors.MissingOperand, file, expr.Token.End())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Assign{Expression: expr}, nil
|
return &Assign{Expression: expr}, nil
|
||||||
|
|
||||||
case IsFunctionCall(expr):
|
|
||||||
return &Call{Expression: expr}, nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(&errors.InvalidInstruction{Instruction: tokens.Text(source)}, nil, tokens[0].Position)
|
return nil, errors.New(&errors.InvalidInstruction{Instruction: tokens.Text(file.Bytes)}, file, tokens[0].Position)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,44 +3,45 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/fs"
|
||||||
"git.akyoto.dev/cli/q/src/token"
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseKeyword generates a keyword node from an instruction.
|
// parseKeyword generates a keyword node from an instruction.
|
||||||
func parseKeyword(tokens token.List, source []byte, nodes AST) (Node, error) {
|
func parseKeyword(tokens token.List, file *fs.File, nodes AST) (Node, error) {
|
||||||
switch tokens[0].Kind {
|
switch tokens[0].Kind {
|
||||||
case token.Assert:
|
case token.Assert:
|
||||||
if len(tokens) == 1 {
|
if len(tokens) == 1 {
|
||||||
return nil, errors.New(errors.MissingExpression, nil, tokens[0].End())
|
return nil, errors.New(errors.MissingExpression, file, tokens[0].End())
|
||||||
}
|
}
|
||||||
|
|
||||||
condition := expression.Parse(tokens[1:])
|
condition := expression.Parse(tokens[1:])
|
||||||
return &Assert{Condition: condition}, nil
|
return &Assert{Condition: condition}, nil
|
||||||
|
|
||||||
case token.If:
|
case token.If:
|
||||||
blockStart, _, body, err := block(tokens, source)
|
blockStart, _, body, err := block(tokens, file)
|
||||||
condition := expression.Parse(tokens[1:blockStart])
|
condition := expression.Parse(tokens[1:blockStart])
|
||||||
return &If{Condition: condition, Body: body}, err
|
return &If{Condition: condition, Body: body}, err
|
||||||
|
|
||||||
case token.Else:
|
case token.Else:
|
||||||
_, _, body, err := block(tokens, source)
|
_, _, body, err := block(tokens, file)
|
||||||
|
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return nil, errors.New(errors.ExpectedIfBeforeElse, nil, tokens[0].Position)
|
return nil, errors.New(errors.ExpectedIfBeforeElse, file, tokens[0].Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
last := nodes[len(nodes)-1]
|
last := nodes[len(nodes)-1]
|
||||||
ifNode, exists := last.(*If)
|
ifNode, exists := last.(*If)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New(errors.ExpectedIfBeforeElse, nil, tokens[0].Position)
|
return nil, errors.New(errors.ExpectedIfBeforeElse, file, tokens[0].Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
ifNode.Else = body
|
ifNode.Else = body
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
||||||
case token.For:
|
case token.For:
|
||||||
blockStart, _, body, err := block(tokens, source)
|
blockStart, _, body, err := block(tokens, file)
|
||||||
head := tokens[1:blockStart]
|
head := tokens[1:blockStart]
|
||||||
|
|
||||||
loop := &For{
|
loop := &For{
|
||||||
@ -51,7 +52,7 @@ func parseKeyword(tokens token.List, source []byte, nodes AST) (Node, error) {
|
|||||||
return loop, err
|
return loop, err
|
||||||
|
|
||||||
case token.Loop:
|
case token.Loop:
|
||||||
_, _, body, err := block(tokens, source)
|
_, _, body, err := block(tokens, file)
|
||||||
return &Loop{Body: body}, err
|
return &Loop{Body: body}, err
|
||||||
|
|
||||||
case token.Return:
|
case token.Return:
|
||||||
@ -67,42 +68,23 @@ func parseKeyword(tokens token.List, source []byte, nodes AST) (Node, error) {
|
|||||||
blockEnd := tokens.LastIndexKind(token.BlockEnd)
|
blockEnd := tokens.LastIndexKind(token.BlockEnd)
|
||||||
|
|
||||||
if blockStart == -1 {
|
if blockStart == -1 {
|
||||||
return nil, errors.New(errors.MissingBlockStart, nil, tokens[0].End())
|
return nil, errors.New(errors.MissingBlockStart, file, tokens[0].End())
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockEnd == -1 {
|
if blockEnd == -1 {
|
||||||
return nil, errors.New(errors.MissingBlockEnd, nil, tokens[len(tokens)-1].End())
|
return nil, errors.New(errors.MissingBlockEnd, file, tokens[len(tokens)-1].End())
|
||||||
}
|
}
|
||||||
|
|
||||||
body := tokens[blockStart+1 : blockEnd]
|
body := tokens[blockStart+1 : blockEnd]
|
||||||
|
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return nil, errors.New(errors.EmptySwitch, nil, tokens[0].Position)
|
return nil, errors.New(errors.EmptySwitch, file, tokens[0].Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
cases, err := parseSwitch(body, source)
|
cases, err := parseCases(body, file)
|
||||||
return &Switch{Cases: cases}, err
|
return &Switch{Cases: cases}, err
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(&errors.KeywordNotImplemented{Keyword: tokens[0].Text(source)}, nil, tokens[0].Position)
|
return nil, errors.New(&errors.KeywordNotImplemented{Keyword: tokens[0].Text(file.Bytes)}, file, tokens[0].Position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// block retrieves the start and end position of a block.
|
|
||||||
func block(tokens token.List, source []byte) (blockStart int, blockEnd int, body AST, err error) {
|
|
||||||
blockStart = tokens.IndexKind(token.BlockStart)
|
|
||||||
blockEnd = tokens.LastIndexKind(token.BlockEnd)
|
|
||||||
|
|
||||||
if blockStart == -1 {
|
|
||||||
err = errors.New(errors.MissingBlockStart, nil, tokens[0].End())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockEnd == -1 {
|
|
||||||
err = errors.New(errors.MissingBlockEnd, nil, tokens[len(tokens)-1].End())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err = Parse(tokens[blockStart+1:blockEnd], source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -2,18 +2,16 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.akyoto.dev/cli/q/src/ast"
|
"git.akyoto.dev/cli/q/src/ast"
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
|
||||||
"git.akyoto.dev/cli/q/src/token"
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompileTokens compiles a token list.
|
// CompileTokens compiles a token list.
|
||||||
func (f *Function) CompileTokens(tokens []token.Token) error {
|
func (f *Function) CompileTokens(tokens []token.Token) error {
|
||||||
body, err := ast.Parse(tokens, f.File.Bytes)
|
tree, err := ast.Parse(tokens, f.File)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err.(*errors.Error).File = f.File
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.CompileAST(body)
|
return f.CompileAST(tree)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user