201 lines
3.9 KiB
Go
201 lines
3.9 KiB
Go
package scanner
|
|
|
|
import (
|
|
"git.akyoto.dev/cli/q/src/core"
|
|
"git.akyoto.dev/cli/q/src/errors"
|
|
"git.akyoto.dev/cli/q/src/fs"
|
|
"git.akyoto.dev/cli/q/src/scope"
|
|
"git.akyoto.dev/cli/q/src/token"
|
|
"git.akyoto.dev/cli/q/src/types"
|
|
"git.akyoto.dev/cli/q/src/x64"
|
|
)
|
|
|
|
// scanFunction scans a function.
|
|
func (s *Scanner) scanFunction(file *fs.File, tokens token.List, i int) (int, error) {
|
|
var (
|
|
groupLevel = 0
|
|
blockLevel = 0
|
|
nameStart = i
|
|
paramsStart = -1
|
|
paramsEnd = -1
|
|
bodyStart = -1
|
|
typeStart = -1
|
|
typeEnd = -1
|
|
)
|
|
|
|
i++
|
|
|
|
// Function parameters
|
|
for i < len(tokens) {
|
|
if tokens[i].Kind == token.GroupStart {
|
|
groupLevel++
|
|
i++
|
|
|
|
if groupLevel == 1 {
|
|
paramsStart = i
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if tokens[i].Kind == token.GroupEnd {
|
|
groupLevel--
|
|
|
|
if groupLevel < 0 {
|
|
return i, errors.New(errors.MissingGroupStart, file, tokens[i].Position)
|
|
}
|
|
|
|
if groupLevel == 0 {
|
|
paramsEnd = i
|
|
i++
|
|
break
|
|
}
|
|
|
|
i++
|
|
continue
|
|
}
|
|
|
|
if tokens[i].Kind == token.Invalid {
|
|
return i, errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
|
}
|
|
|
|
if tokens[i].Kind == token.EOF {
|
|
if groupLevel > 0 {
|
|
return i, errors.New(errors.MissingGroupEnd, file, tokens[i].Position)
|
|
}
|
|
|
|
if paramsStart == -1 {
|
|
return i, errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
|
}
|
|
|
|
return i, nil
|
|
}
|
|
|
|
if groupLevel > 0 {
|
|
i++
|
|
continue
|
|
}
|
|
|
|
return i, errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
|
}
|
|
|
|
// Return type
|
|
if i < len(tokens) && tokens[i].Kind == token.ReturnType {
|
|
typeStart = i + 1
|
|
|
|
for i < len(tokens) && tokens[i].Kind != token.BlockStart {
|
|
i++
|
|
}
|
|
|
|
typeEnd = i
|
|
}
|
|
|
|
// Function definition
|
|
for i < len(tokens) {
|
|
if tokens[i].Kind == token.ReturnType {
|
|
i++
|
|
continue
|
|
}
|
|
|
|
if tokens[i].Kind == token.BlockStart {
|
|
blockLevel++
|
|
i++
|
|
|
|
if blockLevel == 1 {
|
|
bodyStart = i
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if tokens[i].Kind == token.BlockEnd {
|
|
blockLevel--
|
|
|
|
if blockLevel < 0 {
|
|
return i, errors.New(errors.MissingBlockStart, file, tokens[i].Position)
|
|
}
|
|
|
|
if blockLevel == 0 {
|
|
break
|
|
}
|
|
|
|
i++
|
|
continue
|
|
}
|
|
|
|
if tokens[i].Kind == token.Invalid {
|
|
return i, errors.New(&errors.InvalidCharacter{Character: tokens[i].Text(file.Bytes)}, file, tokens[i].Position)
|
|
}
|
|
|
|
if tokens[i].Kind == token.EOF {
|
|
if blockLevel > 0 {
|
|
return i, errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
|
}
|
|
|
|
if bodyStart == -1 {
|
|
return i, errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
|
}
|
|
|
|
return i, nil
|
|
}
|
|
|
|
if blockLevel > 0 {
|
|
i++
|
|
continue
|
|
}
|
|
|
|
return i, errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
|
}
|
|
|
|
name := tokens[nameStart].Text(file.Bytes)
|
|
body := tokens[bodyStart:i]
|
|
function := core.NewFunction(file.Package, name, file, body)
|
|
|
|
if typeStart != -1 {
|
|
if tokens[typeStart].Kind == token.GroupStart && tokens[typeEnd-1].Kind == token.GroupEnd {
|
|
typeStart++
|
|
typeEnd--
|
|
}
|
|
|
|
function.ReturnTypes = types.ParseList(tokens[typeStart:typeEnd], file.Bytes)
|
|
}
|
|
|
|
parameters := tokens[paramsStart:paramsEnd]
|
|
count := 0
|
|
|
|
err := parameters.Split(func(tokens token.List) error {
|
|
if len(tokens) < 2 {
|
|
return errors.New(errors.MissingType, file, tokens[0].End())
|
|
}
|
|
|
|
name := tokens[0].Text(file.Bytes)
|
|
dataType := types.Parse(tokens[1:].Text(file.Bytes))
|
|
register := x64.InputRegisters[count]
|
|
uses := token.Count(function.Body, file.Bytes, token.Identifier, name)
|
|
|
|
if uses == 0 && name != "_" {
|
|
return errors.New(&errors.UnusedVariable{Name: name}, file, tokens[0].Position)
|
|
}
|
|
|
|
variable := &scope.Variable{
|
|
Name: name,
|
|
Type: dataType,
|
|
Register: register,
|
|
Alive: uses,
|
|
}
|
|
|
|
function.Parameters = append(function.Parameters, variable)
|
|
function.AddVariable(variable)
|
|
count++
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
|
|
s.functions <- function
|
|
i++
|
|
return i, nil
|
|
}
|