package extgen import ( "bufio" "fmt" "os" "regexp" "strconv" "strings" ) var constRegex = regexp.MustCompile(`//\s*export_php:const$`) var classConstRegex = regexp.MustCompile(`//\s*export_php:classconst\s+(\w+)$`) var constDeclRegex = regexp.MustCompile(`const\s+(\w+)\s*=\s*(.+)`) type ConstantParser struct{} func (cp *ConstantParser) parse(filename string) (constants []phpConstant, err error) { file, err := os.Open(filename) if err != nil { return nil, err } defer func() { e := file.Close() if err == nil { err = e } }() scanner := bufio.NewScanner(file) lineNumber := 0 expectConstDecl := false expectClassConstDecl := false currentClassName := "" currentConstantValue := 0 inConstBlock := false exportAllInBlock := false lastConstValue := "" lastConstWasIota := false for scanner.Scan() { lineNumber++ line := strings.TrimSpace(scanner.Text()) if constRegex.MatchString(line) { expectConstDecl = true expectClassConstDecl = false currentClassName = "" continue } if matches := classConstRegex.FindStringSubmatch(line); len(matches) == 2 { expectClassConstDecl = true expectConstDecl = false currentClassName = matches[1] continue } if strings.HasPrefix(line, "const (") { inConstBlock = true if expectConstDecl || expectClassConstDecl { exportAllInBlock = true } continue } if inConstBlock && line == ")" { inConstBlock = false exportAllInBlock = false expectConstDecl = false expectClassConstDecl = false currentClassName = "" lastConstValue = "" lastConstWasIota = false continue } if (expectConstDecl || expectClassConstDecl) && strings.HasPrefix(line, "const ") && !inConstBlock { matches := constDeclRegex.FindStringSubmatch(line) if len(matches) == 3 { name := matches[1] value := strings.TrimSpace(matches[2]) constant := phpConstant{ Name: name, Value: value, IsIota: value == "iota", lineNumber: lineNumber, ClassName: currentClassName, } constant.PhpType = determineConstantType(value) if constant.IsIota { constant.Value = fmt.Sprintf("%d", currentConstantValue) constant.PhpType = phpInt currentConstantValue++ lastConstWasIota = true lastConstValue = constant.Value } constants = append(constants, constant) } else { return nil, fmt.Errorf("invalid constant declaration at line %d: %s", lineNumber, line) } expectConstDecl = false expectClassConstDecl = false } else if inConstBlock && (expectConstDecl || expectClassConstDecl || exportAllInBlock) { constBlockDeclRegex := regexp.MustCompile(`^(\w+)\s*=\s*(.+)$`) if matches := constBlockDeclRegex.FindStringSubmatch(line); len(matches) == 3 { name := matches[1] value := strings.TrimSpace(matches[2]) constant := phpConstant{ Name: name, Value: value, IsIota: value == "iota", lineNumber: lineNumber, ClassName: currentClassName, } constant.PhpType = determineConstantType(value) if constant.IsIota { constant.Value = fmt.Sprintf("%d", currentConstantValue) constant.PhpType = phpInt currentConstantValue++ lastConstWasIota = true lastConstValue = constant.Value } else { lastConstWasIota = false lastConstValue = value } constants = append(constants, constant) expectConstDecl = false expectClassConstDecl = false } else { constNameRegex := regexp.MustCompile(`^(\w+)$`) if matches := constNameRegex.FindStringSubmatch(line); len(matches) == 2 { name := matches[1] constant := phpConstant{ Name: name, Value: "", IsIota: lastConstWasIota, lineNumber: lineNumber, ClassName: currentClassName, } if lastConstWasIota { constant.Value = fmt.Sprintf("%d", currentConstantValue) constant.PhpType = phpInt currentConstantValue++ lastConstValue = constant.Value } else { constant.Value = lastConstValue constant.PhpType = determineConstantType(lastConstValue) } constants = append(constants, constant) expectConstDecl = false expectClassConstDecl = false } } } else if (expectConstDecl || expectClassConstDecl) && !strings.HasPrefix(line, "//") && line != "" && !inConstBlock { // we expected a const declaration but found something else, reset expectConstDecl = false expectClassConstDecl = false currentClassName = "" } } return constants, scanner.Err() } // determineConstantType analyzes the value and determines its type func determineConstantType(value string) phpType { value = strings.TrimSpace(value) if (strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`)) || (strings.HasPrefix(value, "`") && strings.HasSuffix(value, "`")) { return phpString } if value == "true" || value == "false" { return phpBool } // check for integer literals, including hex, octal, binary if _, err := strconv.ParseInt(value, 0, 64); err == nil { return phpInt } if _, err := strconv.ParseFloat(value, 64); err == nil { return phpFloat } return phpInt }