Files
2026-05-26 19:21:36 +10:00

388 lines
23 KiB
C

#include "../../../Headers/scc_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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;
}