#include "../../../Headers/scc_core.h" #include #include #include #include static bool is_rule_name(scc_rules* rules, const char* name) { for (uint64_t i = 0; i < rules->rule_count; i++) { if (strcmp(rules->rules[i].node_type_name, name) == 0) { return true; } } return false; } static void print_safe_c_string(FILE* f, const char* str) { fputc('"', f); for (int i = 0; str[i] != '\0'; i++) { if (str[i] == '"') { fprintf(f, "\\\""); } else if (str[i] == '\\') { fprintf(f, "\\\\"); } else { fputc(str[i], f); } } fputc('"', f); } bool scc_translate_to_file_csharp(scc_options *options, scc_rules *rules, FILE *output_file) { if (!options || !rules || !output_file) return false; char* ns_name = (options->namespace_name && strlen(options->namespace_name) > 0) ? options->namespace_name : "SCCGenerated"; char* class_name = (options->class_name && strlen(options->class_name) > 0) ? options->class_name : "SCC"; char* data_type = (options->data_type_name && strlen(options->data_type_name) > 0) ? options->data_type_name : "SyntaxNode"; char* prefix = options->prefix ? options->prefix : ""; char* slex_ns = (options->slex_namespace_name && strlen(options->slex_namespace_name) > 0) ? options->slex_namespace_name : "SLexGenerated"; char* slex_class = (options->slex_class_name && strlen(options->slex_class_name) > 0) ? options->slex_class_name : "SLex"; char* slex_prefix = options->slex_prefix ? options->slex_prefix : ""; char* slex_data_type = (options->slex_data_type_name && strlen(options->slex_data_type_name) > 0) ? options->slex_data_type_name : "Segment"; // 1. Gather all unique terminal tags/ids used in the rules char** terminals = NULL; uint64_t terminal_count = 0; for (uint64_t r = 0; r < rules->rule_count; r++) { scc_rule* rule = &rules->rules[r]; for (uint64_t m = 0; m < rule->matching_count; m++) { scc_matching* matching = rule->matchings[m]; for (uint64_t i = 0; i < matching->match_id_count; i++) { char* match_id = matching->match_ids[i]; size_t len = strlen(match_id); bool is_literal = len >= 2 && ((match_id[0] == '"' && match_id[len-1] == '"') || (match_id[0] == '\'' && match_id[len-1] == '\'')); if (!is_literal && !is_rule_name(rules, match_id)) { // Check if already in list bool exists = false; for (uint64_t j = 0; j < terminal_count; j++) { if (strcmp(terminals[j], match_id) == 0) { exists = true; break; } } if (!exists) { terminal_count++; terminals = (char**)realloc(terminals, terminal_count * sizeof(char*)); terminals[terminal_count - 1] = match_id; } } } } } // Write imports fprintf(output_file, "using System;\n"); fprintf(output_file, "using System.Collections.Generic;\n"); fprintf(output_file, "using %s;\n\n", slex_ns); // Open Namespace fprintf(output_file, "namespace %s\n{\n", ns_name); // SyntaxId enum fprintf(output_file, " public enum %sSyntaxId\n {\n", prefix); fprintf(output_file, " Default = 0,\n"); for (uint64_t i = 0; i < rules->syntax_id_count; i++) { fprintf(output_file, " %s,\n", rules->syntax_ids[i]); } fprintf(output_file, " }\n\n"); // EnclosureType enum fprintf(output_file, " public enum %sEnclosureType\n {\n", prefix); fprintf(output_file, " Node,\n"); fprintf(output_file, " Segment\n"); fprintf(output_file, " }\n\n"); // SyntaxNodeEnclosure class fprintf(output_file, " public class %sSyntaxNodeEnclosure\n {\n", prefix); fprintf(output_file, " public %sEnclosureType EnclosureType;\n", prefix); fprintf(output_file, " public %s? Node;\n", data_type); fprintf(output_file, " public %s? Segment;\n\n", slex_data_type); fprintf(output_file, " public %sSyntaxNodeEnclosure Clone()\n {\n", prefix); fprintf(output_file, " return new %sSyntaxNodeEnclosure\n {\n", prefix); fprintf(output_file, " EnclosureType = this.EnclosureType,\n"); fprintf(output_file, " Node = this.Node?.Clone(),\n"); fprintf(output_file, " Segment = this.Segment != null ? CloneSegment(this.Segment) : null\n"); fprintf(output_file, " };\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " private static %s CloneSegment(%s src)\n {\n", slex_data_type, slex_data_type); fprintf(output_file, " return new %s\n {\n", slex_data_type); fprintf(output_file, " Content = src.Content,\n"); fprintf(output_file, " FileName = src.FileName,\n"); fprintf(output_file, " Line = src.Line,\n"); fprintf(output_file, " Column = src.Column,\n"); fprintf(output_file, " Tag = src.Tag,\n"); fprintf(output_file, " Id = src.Id\n"); fprintf(output_file, " };\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); // SyntaxNode class fprintf(output_file, " public class %s\n {\n", data_type); fprintf(output_file, " public %s? Parent;\n", data_type); fprintf(output_file, " public string SyntaxName = string.Empty;\n"); fprintf(output_file, " public %sSyntaxId Id;\n", prefix); fprintf(output_file, " public List<%sSyntaxNodeEnclosure> Children = new List<%sSyntaxNodeEnclosure>();\n\n", prefix, prefix); fprintf(output_file, " public %s Clone()\n {\n", data_type); fprintf(output_file, " var dst = new %s\n {\n", data_type); fprintf(output_file, " SyntaxName = this.SyntaxName,\n"); fprintf(output_file, " Id = this.Id\n"); fprintf(output_file, " };\n"); fprintf(output_file, " foreach (var child in this.Children)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " var clonedChild = child.Clone();\n"); fprintf(output_file, " dst.Children.Add(clonedChild);\n"); fprintf(output_file, " if (clonedChild.EnclosureType == %sEnclosureType.Node && clonedChild.Node != null)\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " clonedChild.Node.Parent = dst;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " return dst;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); // SCC class fprintf(output_file, " public class %s\n {\n", class_name); // Inner Rule structures for representation fprintf(output_file, " private class MatchingRuleDef\n {\n"); fprintf(output_file, " public string TargetSyntaxId = string.Empty;\n"); fprintf(output_file, " public string[] MatchIds = new string[0];\n"); fprintf(output_file, " public string[] UsingMatchIds = new string[0];\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " private class RuleDef\n {\n"); fprintf(output_file, " public string NodeTypeTypeName = string.Empty;\n"); fprintf(output_file, " public MatchingRuleDef[] Matchings = new MatchingRuleDef[0];\n"); fprintf(output_file, " }\n\n"); // RULES definition fprintf(output_file, " private static readonly RuleDef[] RULES = new RuleDef[]\n {\n"); for (uint64_t r = 0; r < rules->rule_count; r++) { scc_rule* rule = &rules->rules[r]; fprintf(output_file, " new RuleDef\n {\n"); fprintf(output_file, " NodeTypeTypeName = "); print_safe_c_string(output_file, rule->node_type_name); fprintf(output_file, ",\n"); fprintf(output_file, " Matchings = new MatchingRuleDef[]\n {\n"); for (uint64_t m = 0; m < rule->matching_count; m++) { scc_matching* matching = rule->matchings[m]; fprintf(output_file, " new MatchingRuleDef\n {\n"); fprintf(output_file, " TargetSyntaxId = "); print_safe_c_string(output_file, matching->target_syntax_id ? matching->target_syntax_id : "skip"); fprintf(output_file, ",\n"); fprintf(output_file, " MatchIds = new string[] { "); for (uint64_t i = 0; i < matching->match_id_count; i++) { print_safe_c_string(output_file, matching->match_ids[i]); if (i < matching->match_id_count - 1) fprintf(output_file, ", "); } fprintf(output_file, " },\n"); fprintf(output_file, " UsingMatchIds = new string[] { "); for (uint64_t i = 0; i < matching->using_match_id_count; i++) { print_safe_c_string(output_file, matching->using_match_id[i]); if (i < matching->using_match_id_count - 1) fprintf(output_file, ", "); } fprintf(output_file, " }\n"); fprintf(output_file, " }"); if (m < rule->matching_count - 1) fprintf(output_file, ",\n"); else fprintf(output_file, "\n"); } fprintf(output_file, " }\n"); fprintf(output_file, " }"); if (r < rules->rule_count - 1) fprintf(output_file, ",\n"); else fprintf(output_file, "\n"); } fprintf(output_file, " };\n\n"); // MatchElement helper fprintf(output_file, " private static bool MatchElement(%sSyntaxNodeEnclosure enc, string pattern)\n {\n", prefix); fprintf(output_file, " if (enc == null || pattern == null) return false;\n"); fprintf(output_file, " if (enc.EnclosureType == %sEnclosureType.Node)\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " return enc.Node != null && enc.Node.SyntaxName == pattern;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " else\n"); fprintf(output_file, " {\n"); fprintf(output_file, " if (enc.Segment == null) return false;\n"); fprintf(output_file, " if (pattern.Length >= 2 && ((pattern[0] == '\"' && pattern[pattern.Length - 1] == '\"') || (pattern[0] == '\\'' && pattern[pattern.Length - 1] == '\\'')))\n"); fprintf(output_file, " {\n"); fprintf(output_file, " string stripped = pattern.Substring(1, pattern.Length - 2);\n"); fprintf(output_file, " return enc.Segment.Content == stripped;\n"); fprintf(output_file, " }\n"); for (uint64_t i = 0; i < terminal_count; i++) { fprintf(output_file, " if (pattern == \"%s\")\n", terminals[i]); fprintf(output_file, " {\n"); fprintf(output_file, " return enc.Segment.Tag == %sTag.%s || enc.Segment.Id == %sId.%s;\n", slex_data_type, terminals[i], slex_data_type, terminals[i]); fprintf(output_file, " }\n"); } fprintf(output_file, " return false;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); if (terminals) free(terminals); // GetSyntaxId helper fprintf(output_file, " private static %sSyntaxId GetSyntaxId(string name)\n {\n", prefix); fprintf(output_file, " if (Enum.TryParse<%sSyntaxId>(name, out var id))\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " return id;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " return %sSyntaxId.Default;\n", prefix); fprintf(output_file, " }\n\n"); // Public Parse method fprintf(output_file, " public bool Parse(%s head, out %s? root)\n {\n", slex_data_type, data_type); fprintf(output_file, " root = null;\n"); fprintf(output_file, " if (head == null) return false;\n"); fprintf(output_file, " var stack = new List<%sSyntaxNodeEnclosure>();\n", prefix); fprintf(output_file, " return ParseStep(stack, head, out root, 0);\n"); fprintf(output_file, " }\n\n"); // ParseStep recursive method fprintf(output_file, " private static bool ParseStep(List<%sSyntaxNodeEnclosure> stack, %s? input, out %s? result, int depth)\n {\n", prefix, slex_data_type, data_type); fprintf(output_file, " result = null;\n"); fprintf(output_file, " if (depth > 2000) return false;\n\n"); fprintf(output_file, " if (input == null)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " if (stack.Count == 1 && stack[0].EnclosureType == %sEnclosureType.Node)\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " var node = stack[0].Node;\n"); fprintf(output_file, " if (node != null && node.SyntaxName == RULES[0].NodeTypeTypeName)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " result = node;\n"); fprintf(output_file, " return true;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); // Try Reductions fprintf(output_file, " for (int r = 0; r < RULES.Length; r++)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " var rule = RULES[r];\n"); fprintf(output_file, " for (int m = 0; m < rule.Matchings.Length; m++)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " var match = rule.Matchings[m];\n"); fprintf(output_file, " if (stack.Count >= match.MatchIds.Length)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " bool matchOk = true;\n"); fprintf(output_file, " int stackOffset = stack.Count - match.MatchIds.Length;\n"); fprintf(output_file, " for (int i = 0; i < match.MatchIds.Length; i++)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " if (!MatchElement(stack[stackOffset + i], match.MatchIds[i]))\n"); fprintf(output_file, " {\n"); fprintf(output_file, " matchOk = false;\n"); fprintf(output_file, " break;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " if (matchOk)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " var newStack = new List<%sSyntaxNodeEnclosure>();\n", prefix); fprintf(output_file, " for (int i = 0; i < stackOffset; i++)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " newStack.Add(stack[i]);\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " bool skipPush = false;\n"); fprintf(output_file, " %sSyntaxNodeEnclosure? reducedEnc = null;\n\n", prefix); fprintf(output_file, " if (match.TargetSyntaxId == \"skip\")\n"); fprintf(output_file, " {\n"); fprintf(output_file, " skipPush = true;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " else if (match.TargetSyntaxId == \"append_as_child\")\n"); fprintf(output_file, " {\n"); fprintf(output_file, " if (match.MatchIds.Length >= 1 && stack[stackOffset].EnclosureType == %sEnclosureType.Node && stack[stackOffset].Node != null)\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " var parentNode = stack[stackOffset].Node;\n"); fprintf(output_file, " var clonedParent = parentNode.Clone();\n\n"); fprintf(output_file, " if (match.UsingMatchIds.Length >= 1)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " int childIdx = int.Parse(match.UsingMatchIds[0].Substring(1));\n"); fprintf(output_file, " var childEnc = stack[stackOffset + childIdx].Clone();\n"); fprintf(output_file, " clonedParent.Children.Add(childEnc);\n"); fprintf(output_file, " if (childEnc.EnclosureType == %sEnclosureType.Node && childEnc.Node != null)\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " childEnc.Node.Parent = clonedParent;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " reducedEnc = new %sSyntaxNodeEnclosure\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " EnclosureType = %sEnclosureType.Node,\n", prefix); fprintf(output_file, " Node = clonedParent\n"); fprintf(output_file, " };\n"); fprintf(output_file, " }\n"); fprintf(output_file, " else\n"); fprintf(output_file, " {\n"); fprintf(output_file, " matchOk = false;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " else\n"); fprintf(output_file, " {\n"); fprintf(output_file, " var newNode = new %s\n", data_type); fprintf(output_file, " {\n"); fprintf(output_file, " Id = GetSyntaxId(match.TargetSyntaxId),\n"); fprintf(output_file, " SyntaxName = rule.NodeTypeTypeName\n"); fprintf(output_file, " };\n\n"); fprintf(output_file, " for (int i = 0; i < match.UsingMatchIds.Length; i++)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " int idx = int.Parse(match.UsingMatchIds[i].Substring(1));\n"); fprintf(output_file, " var childEnc = stack[stackOffset + idx].Clone();\n"); fprintf(output_file, " newNode.Children.Add(childEnc);\n"); fprintf(output_file, " if (childEnc.EnclosureType == %sEnclosureType.Node && childEnc.Node != null)\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " childEnc.Node.Parent = newNode;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " reducedEnc = new %sSyntaxNodeEnclosure\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " EnclosureType = %sEnclosureType.Node,\n", prefix); fprintf(output_file, " Node = newNode\n"); fprintf(output_file, " };\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " if (matchOk)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " if (!skipPush && reducedEnc != null)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " newStack.Add(reducedEnc);\n"); fprintf(output_file, " }\n"); fprintf(output_file, " if (ParseStep(newStack, input, out result, depth + 1))\n"); fprintf(output_file, " {\n"); fprintf(output_file, " return true;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); // Try Shift fprintf(output_file, " if (input != null)\n"); fprintf(output_file, " {\n"); fprintf(output_file, " var newStack = new List<%sSyntaxNodeEnclosure>(stack);\n", prefix); fprintf(output_file, " newStack.Add(new %sSyntaxNodeEnclosure\n", prefix); fprintf(output_file, " {\n"); fprintf(output_file, " EnclosureType = %sEnclosureType.Segment,\n", prefix); fprintf(output_file, " Segment = input\n"); fprintf(output_file, " });\n\n"); fprintf(output_file, " if (ParseStep(newStack, input.Next, out result, depth + 1))\n"); fprintf(output_file, " {\n"); fprintf(output_file, " return true;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n\n"); fprintf(output_file, " return false;\n"); fprintf(output_file, " }\n"); fprintf(output_file, " }\n"); // Close Namespace fprintf(output_file, "}\n"); return true; }