diff --git a/GDWeave.Dumper/CodeGenerator.cs b/GDWeave.Dumper/CodeGenerator.cs index 153f252..5e39610 100644 --- a/GDWeave.Dumper/CodeGenerator.cs +++ b/GDWeave.Dumper/CodeGenerator.cs @@ -2,16 +2,161 @@ using GDWeave.Godot.Variants; public class CodeGenerator(List tokens, List identifiers) { + Dictionary builtinFunctionLiteral = new() { + [(int) BuiltinFunction.MathSin] = "sin", + [(int) BuiltinFunction.MathCos] = "cos", + [(int) BuiltinFunction.MathTan] = "tan", + [(int) BuiltinFunction.MathSinh] = "sinh", + [(int) BuiltinFunction.MathCosh] = "cosh", + [(int) BuiltinFunction.MathTanh] = "tanh", + [(int) BuiltinFunction.MathAsin] = "asin", + [(int) BuiltinFunction.MathAcos] = "acos", + [(int) BuiltinFunction.MathAtan] = "atan", + [(int) BuiltinFunction.MathAtan2] = "atan2", + [(int) BuiltinFunction.MathSqrt] = "sqrt", + [(int) BuiltinFunction.MathFmod] = "fmod", + [(int) BuiltinFunction.MathFposmod] = "fposmod", + [(int) BuiltinFunction.MathPosmod] = "posmod", + [(int) BuiltinFunction.MathFloor] = "floor", + [(int) BuiltinFunction.MathCeil] = "ceil", + [(int) BuiltinFunction.MathRound] = "round", + [(int) BuiltinFunction.MathAbs] = "abs", + [(int) BuiltinFunction.MathSign] = "sign", + [(int) BuiltinFunction.MathPow] = "pow", + [(int) BuiltinFunction.MathLog] = "log", + [(int) BuiltinFunction.MathExp] = "exp", + [(int) BuiltinFunction.MathIsnan] = "is_nan", + [(int) BuiltinFunction.MathIsinf] = "is_inf", + [(int) BuiltinFunction.MathIsequalapprox] = "is_equal_approx", + [(int) BuiltinFunction.MathIszeroapprox] = "is_zero_approx", + [(int) BuiltinFunction.MathEase] = "ease", + [(int) BuiltinFunction.MathDecimals] = "decimals", + [(int) BuiltinFunction.MathStepDecimals] = "step_decimals", + [(int) BuiltinFunction.MathStepify] = "stepify", + [(int) BuiltinFunction.MathLerp] = "lerp", + [(int) BuiltinFunction.MathLerpAngle] = "lerp_angle", + [(int) BuiltinFunction.MathInverseLerp] = "inverse_lerp", + [(int) BuiltinFunction.MathRangeLerp] = "range_lerp", + [(int) BuiltinFunction.MathSmoothstep] = "smoothstep", + [(int) BuiltinFunction.MathMoveToward] = "move_toward", + [(int) BuiltinFunction.MathDectime] = "dectime", + [(int) BuiltinFunction.MathRandomize] = "randomize", + [(int) BuiltinFunction.MathRand] = "randi", + [(int) BuiltinFunction.MathRandf] = "randf", + [(int) BuiltinFunction.MathRandom] = "rand_range", + [(int) BuiltinFunction.MathSeed] = "seed", + [(int) BuiltinFunction.MathSeed] = "rand_seed", + [(int) BuiltinFunction.MathDeg2Rad] = "deg2rad", + [(int) BuiltinFunction.MathRad2Deg] = "rad2deg", + [(int) BuiltinFunction.MathLinear2Db] = "linear2db", + [(int) BuiltinFunction.MathDb2Linear] = "db2linear", + [(int) BuiltinFunction.MathPolar2Cartesian] = "polar2cartesian", + [(int) BuiltinFunction.MathCartesian2Polar] = "cartesian2polar", + [(int) BuiltinFunction.MathWrap] = "wrapi", + [(int) BuiltinFunction.MathWrap] = "wrapf", + [(int) BuiltinFunction.LogicMax] = "max", + [(int) BuiltinFunction.LogicMin] = "min", + [(int) BuiltinFunction.LogicClamp] = "clamp", + [(int) BuiltinFunction.LogicNearestPo2] = "nearest_po2", + [(int) BuiltinFunction.ObjWeakref] = "weakref", + [(int) BuiltinFunction.FuncFuncref] = "funcref", + [(int) BuiltinFunction.TypeConvert] = "convert", + [(int) BuiltinFunction.TypeOf] = "typeof", + [(int) BuiltinFunction.TypeExists] = "type_exists", + [(int) BuiltinFunction.TextChar] = "char", + [(int) BuiltinFunction.TextOrd] = "ord", + [(int) BuiltinFunction.TextStr] = "str", + [(int) BuiltinFunction.TextPrint] = "print", + [(int) BuiltinFunction.TextPrintTabbed] = "printt", + [(int) BuiltinFunction.TextPrintSpaced] = "prints", + [(int) BuiltinFunction.TextPrinterr] = "printerr", + [(int) BuiltinFunction.TextPrintraw] = "printraw", + [(int) BuiltinFunction.TextPrintDebug] = "print_debug", + [(int) BuiltinFunction.PushError] = "push_error", + [(int) BuiltinFunction.PushWarning] = "push_warning", + [(int) BuiltinFunction.VarToStr] = "var2str", + [(int) BuiltinFunction.StrToVar] = "str2var", + [(int) BuiltinFunction.VarToBytes] = "var2bytes", + [(int) BuiltinFunction.BytesToVar] = "bytes2var", + [(int) BuiltinFunction.GenRange] = "range", + [(int) BuiltinFunction.ResourceLoad] = "load", + [(int) BuiltinFunction.Inst2Dict] = "inst2dict", + [(int) BuiltinFunction.Dict2Inst] = "dict2inst", + [(int) BuiltinFunction.ValidateJson] = "validate_json", + [(int) BuiltinFunction.ParseJson] = "parse_json", + [(int) BuiltinFunction.ToJson] = "to_json", + [(int) BuiltinFunction.Hash] = "hash", + [(int) BuiltinFunction.Color8] = "Color8", + [(int) BuiltinFunction.Colorn] = "ColorN", + [(int) BuiltinFunction.PrintStack] = "print_stack", + [(int) BuiltinFunction.GetStack] = "get_stack", + [(int) BuiltinFunction.InstanceFromId] = "instance_from_id", + [(int) BuiltinFunction.Len] = "len", + [(int) BuiltinFunction.IsInstanceValid] = "is_instance_valid", + [(int) BuiltinFunction.DeepEqual] = "deep_equal", + [(int) BuiltinFunction.FuncMax] = "MAX", + }; + public void Generate(StreamWriter writer) { - var onNewLine = false; + var whitespaceExclusions = new List( + [ + // Identifiers // + TokenType.BuiltInFunc, + TokenType.PrPreload, + TokenType.BuiltInType, + TokenType.Identifier, + TokenType.Constant, + TokenType.ConstInf, + TokenType.ConstNan, + TokenType.ConstPi, + // Brackets, accessors, etc // + TokenType.BracketOpen, + TokenType.BracketClose, + TokenType.ParenthesisOpen, + TokenType.ParenthesisClose, + TokenType.Period, + TokenType.Dollar, + // Operators, Keywords // + TokenType.OpDiv, + TokenType.OpSub, + TokenType.PrYield, + TokenType.CfElse, + ] + ); + + var binaryOperators = new List( + [ + TokenType.OpIn, + TokenType.OpAnd, + TokenType.OpOr, + TokenType.OpEqual, + TokenType.OpGreater, + TokenType.OpGreaterEqual, + TokenType.OpLess, + TokenType.OpLessEqual, + TokenType.OpNotEqual, + TokenType.OpAssign, + TokenType.OpAdd, + // TokenType.OpSub also used for negative numbers + TokenType.OpMul, + // TokenType.OpDiv also used for get_node paths + TokenType.OpMod, + TokenType.OpAssignAdd, + TokenType.OpAssignSub, + TokenType.OpAssignDiv, + TokenType.OpAssignMul, + TokenType.OpAssignMod, + ] + ); + foreach (var token in tokens) { var tabs = 0u; var gen = this.GenerateToken(token, ref tabs); + var onNewLine = gen == "\n"; - onNewLine = gen == "\n"; - - if (!onNewLine) - { + if (binaryOperators.Contains(token.Type)) { + gen = $" {gen} "; + } else if (!onNewLine && !whitespaceExclusions.Contains(token.Type)) { gen += ' '; } @@ -20,6 +165,17 @@ public void Generate(StreamWriter writer) { for (var i = 0; i < tabs; i++) { writer.Write('\t'); } + + // If & Else keywords toggle for handling if-else and ternary cases + if (token.Type == TokenType.Newline) { + binaryOperators.Remove(TokenType.CfIf); + binaryOperators.Remove(TokenType.CfElse); + } else { + if (!binaryOperators.Contains(TokenType.CfIf)) { + binaryOperators.Add(TokenType.CfIf); + binaryOperators.Add(TokenType.CfElse); + } + } } } @@ -31,12 +187,23 @@ private string GenerateToken(Token token, ref uint tabs) { case TokenType.Identifier: return $"{identifiers[data]}"; case TokenType.Constant: - var constantToken = (ConstantToken)token; + var constantToken = (ConstantToken) token; if ( constantToken.Value is StringVariant stringVariant && stringVariant.GetValue() is string str - ) { return $"\"{str}\""; } - return constantToken.Value.GetValue().ToString() ?? ""; + ) { + return $"\"{str}\""; + } else if (constantToken.Value is BoolVariant boolVariant) { + // Lowercase to match GDScript + return (bool) boolVariant.GetValue() ? "true" : "false"; + } else if (constantToken.Value is RealVariant realVariant) { + // Refrain from omitting trailing zeros + var value = (double) realVariant.GetValue(); + return value.ToString("F"); + } else { + return constantToken.Value.GetValue().ToString() + ?? ""; + } case TokenType.Newline: tabs = token.AssociatedData ?? 0; return "\n"; @@ -45,7 +212,9 @@ constantToken.Value is StringVariant stringVariant case TokenType.BuiltInType: return Enum.GetName((VariantType) data) ?? ""; case TokenType.BuiltInFunc: - return Enum.GetName((BuiltinFunction) data) ?? ""; + return builtinFunctionLiteral.ContainsKey(data) + ? builtinFunctionLiteral[data] + : ""; case TokenType.OpIn: return "in"; case TokenType.OpEqual: @@ -61,11 +230,11 @@ constantToken.Value is StringVariant stringVariant case TokenType.OpGreaterEqual: return ">="; case TokenType.OpAnd: - return "&&"; + return "and"; case TokenType.OpOr: - return "||"; + return "or"; case TokenType.OpNot: - return "!"; + return "not"; case TokenType.OpAdd: return "+"; @@ -143,13 +312,13 @@ constantToken.Value is StringVariant stringVariant return "is"; case TokenType.PrOnready: - return "@onready"; + return "onready"; case TokenType.PrTool: return "@tool"; case TokenType.PrStatic: return "static"; case TokenType.PrExport: - return "@export"; + return "export"; // I believe this is incorrect... // Can't find a good explanation for this token online case TokenType.PrSetget: