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 }