356 lines
9.4 KiB
Go

//Parses perl scripts
package main
import (
"bufio"
"fmt"
"log"
"os"
"regexp"
"strings"
"github.com/pkg/errors"
)
func main() {
path := "../../../zone/embparser_api.cpp"
err := readFile(path)
if err != nil {
log.Panicf("Failed to read file: %s", err.Error())
}
}
type API struct {
Function string
Arguments []*Argument
}
type Argument struct {
Name string
Type string
API *API
}
func readFile(path string) (err error) {
inFile, err := os.Open(path)
if err != nil {
err = errors.Wrap(err, "Failed to open file")
}
defer inFile.Close()
scanner := bufio.NewScanner(inFile)
scanner.Split(bufio.ScanLines)
arguments := map[string][]*Argument{}
functions := []*API{}
reg, err := regexp.Compile(`\]+|\[+|\?+|[...]+`)
if err != nil {
err = errors.Wrap(err, "Failed to compile regex")
return
}
regType, err := regexp.Compile(`(unsigned long|long|int32|bool|uint[0-9]+|int|auto|float|unsigned int|char[ \*]).+([. a-zA-Z]+=)`)
if err != nil {
err = errors.Wrap(err, "Failed to compile type regex")
return
}
lastArguments := []*Argument{}
lastAPI := &API{}
lineNum := 0
for scanner.Scan() {
lineNum++
key := ""
line := scanner.Text()
if len(line) < 1 {
continue
}
if len(lastArguments) > 0 { //existing args to parse
for i, argument := range lastArguments {
key = fmt.Sprintf("ST(%d)", i)
if strings.Contains(line, key) {
//We found a definition argument line
if argument.Type != "" {
continue
}
match := regType.FindStringSubmatch(line)
if len(match) < 2 {
continue
}
//key = `int`
//function = line[strings.Index(line, key)+len(key):]
newType := ""
switch v := strings.TrimSpace(match[1]); v {
case "int":
newType = "int"
case "int32":
newType = "int"
case "float":
newType = "float"
case "unsigned int":
newType = "uint"
case "uint32":
newType = "uint"
case "uint8":
newType = "uint"
case "uint":
newType = "uint"
case "bool":
newType = "bool"
case "uint16":
newType = "uint"
case "long":
newType = "long"
case "unsigned long":
newType = "unsigned long"
case "char":
newType = "string"
case "auto":
//Auto is tricky
if strings.Contains(line, "glm::vec4") {
newType = "float"
}
default:
log.Printf(`Unknown type: "%s" on line %d`, v, lineNum)
}
//log.Println("Found arg type", newType, "on index", i, argument.Name)
lastArguments[i].Type = newType
}
}
}
function := ""
argLine := ""
args := []string{}
//Find line
key = `Perl_croak(aTHX_ "Usage:`
if strings.Contains(line, key) {
function = line[strings.Index(line, key)+len(key):]
}
for _, argument := range lastArguments {
arguments[argument.Name] = append(arguments[argument.Name], argument)
}
lastArguments = []*Argument{}
//Trim off the endings
key = `");`
if strings.Contains(function, key) {
function = function[0:strings.Index(function, key)]
}
//Strip out the arguments
key = `(`
if strings.Contains(function, key) {
argLine = function[strings.Index(function, key)+len(key):]
function = function[0:strings.Index(function, key)]
key = `)`
if strings.Contains(argLine, key) {
argLine = argLine[:strings.Index(argLine, key)]
}
key = `=`
if strings.Contains(argLine, key) {
argLine = argLine[:strings.Index(argLine, key)]
}
argLine = reg.ReplaceAllString(argLine, "")
}
key = `,`
argLine = strings.TrimSpace(argLine)
if strings.Contains(argLine, key) {
args = strings.Split(argLine, key)
}
if len(function) < 1 {
continue
}
newArgs := []string{}
for j, _ := range args {
args[j] = strings.TrimSpace(args[j])
if len(args[j]) == 0 {
continue
}
newArgs = append(newArgs, args[j])
}
lastAPI = &API{
Function: function,
}
for _, arg := range newArgs {
argType, _ := knownTypes[arg]
argument := &Argument{
Name: arg,
Type: argType,
API: lastAPI,
}
lastArguments = append(lastArguments, argument)
}
lastAPI.Arguments = lastArguments
functions = append(functions, lastAPI)
}
foundCount := 0
failCount := 0
for key, val := range arguments {
isMissing := false
line := ""
line = fmt.Sprintf("%s used by %d functions:", key, len(val))
for _, fnc := range val {
line += fmt.Sprintf("%s(%s %s), ", fnc.API.Function, fnc.Type, key)
if fnc.Type == "" {
isMissing = true
}
}
if isMissing {
fmt.Println(line)
failCount++
} else {
foundCount++
}
}
log.Println(foundCount, "functions properly identified,", failCount, "have errors")
line := ""
for _, api := range functions {
line += fmt.Sprintf("void %s(", strings.TrimSpace(api.Function))
for _, argument := range api.Arguments {
line += fmt.Sprintf("%s %s, ", argument.Type, argument.Name)
}
if len(api.Arguments) > 0 {
line = line[0 : len(line)-2]
}
line += ")\n"
}
fmt.Println(line)
return
}
var knownTypes = map[string]string{
"activity_id": "uint",
"alt_mode": "bool",
"anim_num": "int",
"best_z": "float",
"buttons": "int",
"channel_id": "int",
"char_id": "int",
"charges": "int",
"class_id": "int",
"client_name": "string",
"color": "int",
"color_id": "int",
"condition_id": "int",
"copper": "int",
"count": "int",
"debug_level": "int",
"decay_time": "int",
"dest_heading": "float",
"dest_x": "float",
"dest_y": "float",
"dest_z": "float",
"distance": "int",
"door_id": "int",
"doorid": "uint",
"duration": "int",
"effect_id": "int",
"elite_material_id": "int",
"enforce_level_requirement": "bool",
"explore_id": "uint",
"faction_value": "int",
"fade_in": "int",
"fade_out": "int",
"fadeout": "uint",
"firstname": "string",
"from": "string",
"gender_id": "int",
"gold": "int",
"grid_id": "int",
"guild_rank_id": "int",
"heading": "float",
"hero_forge_model_id": "int",
"ignore_quest_update": "bool",
"instance_id": "int",
"int_unused": "int",
"int_value": "int",
"is_enabled": "bool",
"is_strict": "bool",
"item_id": "int",
"key": "string",
"language_id": "int",
"lastname": "string",
"leader_name": "string",
"level": "int",
"link_name": "string",
"macro_id": "int",
"max_level": "int",
"max_x": "float",
"max_y": "float",
"max_z": "float",
"message": "string",
"milliseconds": "int",
"min_level": "int",
"min_x": "float",
"min_y": "float",
"min_z": "float",
"name": "string",
"new_hour": "int",
"new_min": "int",
"node1": "int",
"node2": "int",
"npc_id": "int",
"npc_type_id": "int",
"object_type": "int",
"options": "int",
"platinum": "int",
"popup_id": "int",
"priority": "int",
"quantity": "int",
"race_id": "int",
"remove_item": "bool",
"requested_id": "int",
"reset_base": "bool",
"saveguard": "bool",
"seconds": "int",
"send_to_world": "bool",
"signal_id": "int",
"silent": "bool",
"silver": "int",
"size": "int",
"stat_id": "int",
"str_value": "string",
"subject": "string",
"target_enum": "string",
"target_id": "int",
"task": "int",
"task_id": "uint",
"task_id1": "int",
"task_id10": "int",
"task_id2": "int",
"task_set": "int",
"taskid": "int",
"taskid1": "int",
"taskid2": "int",
"taskid3": "int",
"taskid4": "int",
"teleport": "int",
"temp": "int",
"texture_id": "int",
"theme_id": "int",
"update_world": "int",
"updated_time_till_repop": "uint",
"version": "int",
"wait_ms": "int",
"window_title": "string",
"x": "float",
"y": "float",
"z": "float",
"zone_id": "int",
"zone_short": "string",
`task_id%i`: "int",
}