259 lines
14 KiB
C
259 lines
14 KiB
C
#include "../../../Headers/slex_core.h"
|
|
#include "../../../Headers/slex_regex.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// Helper to check if a tag is mapped to an ID, and return it.
|
|
static const char* get_mapped_id(slex_rules* rules, const char* tag) {
|
|
for (uint64_t i = 0; i < rules->mapping_count; i++) {
|
|
if (strcmp(rules->mappings[i].Tag, tag) == 0) {
|
|
return rules->mappings[i].Id;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Print a string as a safe C# verbatim string literal
|
|
static void print_verbatim_string(FILE* f, const char* str) {
|
|
fprintf(f, "@\"");
|
|
for (int i = 0; str[i] != '\0'; i++) {
|
|
if (str[i] == '"') {
|
|
fprintf(f, "\"\"");
|
|
} else {
|
|
fputc(str[i], f);
|
|
}
|
|
}
|
|
fprintf(f, "\"");
|
|
}
|
|
|
|
bool slex_translate_to_file_csharp(slex_options *options, slex_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 : "SLexGenerated";
|
|
char* class_name = (options->class_name && strlen(options->class_name) > 0) ? options->class_name : "SLexer";
|
|
char* data_type = (options->data_type_name && strlen(options->data_type_name) > 0) ? options->data_type_name : "Segment";
|
|
char* prefix = options->prefix ? options->prefix : "";
|
|
|
|
// Extract C# code block variables & post_processor
|
|
char* variables = "";
|
|
char* post_processor = "";
|
|
for (uint64_t i = 0; i < rules->code_block_count; i++) {
|
|
if (rules->code_blocks[i].target_languge == csharp) {
|
|
if (rules->code_blocks[i].variables) {
|
|
variables = rules->code_blocks[i].variables;
|
|
}
|
|
if (rules->code_blocks[i].post_processor_code) {
|
|
post_processor = rules->code_blocks[i].post_processor_code;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Generate file content
|
|
fprintf(output_file, "using System;\n");
|
|
fprintf(output_file, "using System.IO;\n");
|
|
fprintf(output_file, "using System.Text;\n");
|
|
fprintf(output_file, "using System.Text.RegularExpressions;\n\n");
|
|
|
|
fprintf(output_file, "namespace %s\n{\n", ns_name);
|
|
|
|
// SegmentTag enum
|
|
fprintf(output_file, " public enum %sTag\n {\n", data_type);
|
|
for (uint64_t i = 0; i < rules->rule_count; i++) {
|
|
fprintf(output_file, " %s,\n", rules->rules[i].Tag);
|
|
}
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// SegmentId enum
|
|
fprintf(output_file, " public enum %sId\n {\n", data_type);
|
|
fprintf(output_file, " Default = 0,\n");
|
|
for (uint64_t i = 0; i < rules->mapping_count; i++) {
|
|
bool unique = true;
|
|
for (uint64_t j = 0; j < i; j++) {
|
|
if (strcmp(rules->mappings[j].Id, rules->mappings[i].Id) == 0) {
|
|
unique = false;
|
|
break;
|
|
}
|
|
}
|
|
if (unique) {
|
|
fprintf(output_file, " %s,\n", rules->mappings[i].Id);
|
|
}
|
|
}
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// PostProcessResult enum
|
|
fprintf(output_file, " public enum PostProcessResult\n {\n");
|
|
fprintf(output_file, " Continue,\n");
|
|
fprintf(output_file, " Skip,\n");
|
|
fprintf(output_file, " ContinueWithOutput\n");
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// Segment class
|
|
fprintf(output_file, " public class %s\n {\n", data_type);
|
|
fprintf(output_file, " public string Content { get; set; } = string.Empty;\n");
|
|
fprintf(output_file, " public string FileName { get; set; } = string.Empty;\n");
|
|
fprintf(output_file, " public %s? Prev { get; set; }\n", data_type);
|
|
fprintf(output_file, " public %s? Next { get; set; }\n", data_type);
|
|
fprintf(output_file, " public long Line { get; set; }\n");
|
|
fprintf(output_file, " public long Column { get; set; }\n");
|
|
fprintf(output_file, " public %sTag Tag { get; set; }\n", data_type);
|
|
fprintf(output_file, " public %sId Id { get; set; }\n", data_type);
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// SLexer class
|
|
fprintf(output_file, " public class %s\n {\n", class_name);
|
|
|
|
// User variables
|
|
if (strlen(variables) > 0) {
|
|
fprintf(output_file, " // --- User Variables ---\n%s\n // ----------------------\n\n", variables);
|
|
}
|
|
|
|
// slex_post_process method
|
|
fprintf(output_file, " private PostProcessResult slex_post_process(%s Input, out %s Output)\n {\n", data_type, data_type);
|
|
if (strlen(post_processor) > 0) {
|
|
fprintf(output_file, "%s\n", post_processor);
|
|
} else {
|
|
fprintf(output_file, " Output = Input;\n");
|
|
fprintf(output_file, " return PostProcessResult.Continue;\n");
|
|
}
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// Compiled System.Text.RegularExpressions.Regex rules using the \G anchor
|
|
fprintf(output_file, " private static readonly Regex[] Rules = new Regex[] {\n");
|
|
for (uint64_t i = 0; i < rules->rule_count; i++) {
|
|
fprintf(output_file, " new Regex(@\"\\G\" + ");
|
|
print_verbatim_string(output_file, rules->rules[i].Pattern);
|
|
fprintf(output_file, ", RegexOptions.Compiled)");
|
|
if (i < rules->rule_count - 1) fprintf(output_file, ",\n");
|
|
else fprintf(output_file, "\n");
|
|
}
|
|
fprintf(output_file, " };\n\n");
|
|
|
|
// Tag and ID assignment
|
|
fprintf(output_file, " private void AssignTagAndId(%s node, int ruleIdx)\n {\n", data_type);
|
|
fprintf(output_file, " switch (ruleIdx)\n {\n");
|
|
for (uint64_t i = 0; i < rules->rule_count; i++) {
|
|
fprintf(output_file, " case %d:\n", (int)i);
|
|
fprintf(output_file, " node.Tag = %sTag.%s;\n", data_type, rules->rules[i].Tag);
|
|
const char* mapped_id = get_mapped_id(rules, rules->rules[i].Tag);
|
|
if (mapped_id) {
|
|
fprintf(output_file, " node.Id = %sId.%s;\n", data_type, mapped_id);
|
|
} else {
|
|
fprintf(output_file, " node.Id = %sId.Default;\n", data_type);
|
|
}
|
|
fprintf(output_file, " break;\n");
|
|
}
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// SLex overloads
|
|
// 1. FileInfo
|
|
fprintf(output_file, " public bool %sSLex(FileInfo inputFile, out %s? Head)\n {\n", prefix, data_type);
|
|
fprintf(output_file, " if (inputFile == null)\n {\n");
|
|
fprintf(output_file, " Head = null;\n");
|
|
fprintf(output_file, " return false;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " using (var stream = inputFile.OpenRead())\n {\n");
|
|
fprintf(output_file, " return %sSLex(stream, inputFile.FullName, out Head);\n", prefix);
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// 2. Stream
|
|
fprintf(output_file, " public bool %sSLex(Stream inputStream, out %s? Head)\n {\n", prefix, data_type);
|
|
fprintf(output_file, " return %sSLex(inputStream, string.Empty, out Head);\n", prefix);
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// helper Stream with filename
|
|
fprintf(output_file, " public bool %sSLex(Stream inputStream, string fileName, out %s? Head)\n {\n", prefix, data_type);
|
|
fprintf(output_file, " if (inputStream == null)\n {\n");
|
|
fprintf(output_file, " Head = null;\n");
|
|
fprintf(output_file, " return false;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " using (var reader = new StreamReader(inputStream))\n {\n");
|
|
fprintf(output_file, " return %sSLex(reader.ReadToEnd(), fileName, out Head);\n", prefix);
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// 3. string
|
|
fprintf(output_file, " public bool %sSLex(string inputContent, out %s? Head)\n {\n", prefix, data_type);
|
|
fprintf(output_file, " return %sSLex(inputContent, string.Empty, out Head);\n", prefix);
|
|
fprintf(output_file, " }\n\n");
|
|
|
|
// Core matching loop: string with filename
|
|
fprintf(output_file, " public bool %sSLex(string inputContent, string fileName, out %s? Head)\n {\n", prefix, data_type);
|
|
fprintf(output_file, " Head = null;\n");
|
|
fprintf(output_file, " if (inputContent == null) return false;\n\n");
|
|
fprintf(output_file, " %s? head = null;\n", data_type);
|
|
fprintf(output_file, " %s? tail = null;\n", data_type);
|
|
fprintf(output_file, " int idx = 0;\n");
|
|
fprintf(output_file, " int len = inputContent.Length;\n");
|
|
fprintf(output_file, " long currentLine = 1;\n");
|
|
fprintf(output_file, " long currentCol = 1;\n\n");
|
|
fprintf(output_file, " while (idx < len)\n {\n");
|
|
fprintf(output_file, " int bestRule = -1;\n");
|
|
fprintf(output_file, " int bestLen = 0;\n");
|
|
fprintf(output_file, " string bestVal = \"\";\n");
|
|
fprintf(output_file, " long tokenLine = currentLine;\n");
|
|
fprintf(output_file, " long tokenCol = currentCol;\n\n");
|
|
fprintf(output_file, " for (int r = 0; r < Rules.Length; r++)\n");
|
|
fprintf(output_file, " {\n");
|
|
fprintf(output_file, " var m = Rules[r].Match(inputContent, idx);\n");
|
|
fprintf(output_file, " if (m.Success && m.Length > bestLen)\n");
|
|
fprintf(output_file, " {\n");
|
|
fprintf(output_file, " bestLen = m.Length;\n");
|
|
fprintf(output_file, " bestRule = r;\n");
|
|
fprintf(output_file, " bestVal = m.Value;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n\n");
|
|
fprintf(output_file, " if (bestRule != -1 && bestLen > 0)\n {\n");
|
|
fprintf(output_file, " var node = new %s\n {\n", data_type);
|
|
fprintf(output_file, " Content = bestVal,\n");
|
|
fprintf(output_file, " FileName = fileName,\n");
|
|
fprintf(output_file, " Line = tokenLine,\n");
|
|
fprintf(output_file, " Column = tokenCol\n");
|
|
fprintf(output_file, " };\n");
|
|
fprintf(output_file, " AssignTagAndId(node, bestRule);\n\n");
|
|
fprintf(output_file, " // Update line/col tracker\n");
|
|
fprintf(output_file, " for (int t = idx; t < idx + bestLen; t++)\n {\n");
|
|
fprintf(output_file, " if (inputContent[t] == '\\n')\n {\n");
|
|
fprintf(output_file, " currentLine++;\n");
|
|
fprintf(output_file, " currentCol = 1;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " else\n {\n");
|
|
fprintf(output_file, " currentCol++;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n\n");
|
|
fprintf(output_file, " %s? outputNode;\n", data_type);
|
|
fprintf(output_file, " PostProcessResult pr = slex_post_process(node, out outputNode);\n");
|
|
fprintf(output_file, " if (pr != PostProcessResult.Skip)\n {\n");
|
|
fprintf(output_file, " %s? toAppend = (pr == PostProcessResult.ContinueWithOutput) ? outputNode : node;\n", data_type);
|
|
fprintf(output_file, " if (toAppend != null)\n {\n");
|
|
fprintf(output_file, " if (head == null)\n {\n");
|
|
fprintf(output_file, " head = toAppend;\n");
|
|
fprintf(output_file, " tail = toAppend;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " else\n {\n");
|
|
fprintf(output_file, " tail.Next = toAppend;\n");
|
|
fprintf(output_file, " toAppend.Prev = tail;\n");
|
|
fprintf(output_file, " tail = toAppend;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " idx += bestLen;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " else\n {\n");
|
|
fprintf(output_file, " Console.Error.WriteLine($\"Lexical error at {fileName}:{tokenLine}:{tokenCol} near '{inputContent[idx]}'\");\n");
|
|
fprintf(output_file, " Head = null;\n");
|
|
fprintf(output_file, " return false;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n\n");
|
|
fprintf(output_file, " Head = head;\n");
|
|
fprintf(output_file, " return true;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, "}\n");
|
|
|
|
return true;
|
|
} |