From 89354af95e2aabf1cf4234c919cc70477ea10ee3 Mon Sep 17 00:00:00 2001 From: Creeper Lv Date: Tue, 26 May 2026 19:34:04 +1000 Subject: [PATCH] Added a calculator test program. --- .gitignore | 3 + test/c/calc_driver.c | 111 ++++++++++++++++++ test/calc.scc | 36 ++++++ test/calc.slex | 42 +++++++ test/csharp/SLexSccCalc/Program.cs | 129 +++++++++++++++++++++ test/csharp/SLexSccCalc/SLexSccCalc.csproj | 10 ++ 6 files changed, 331 insertions(+) create mode 100644 test/c/calc_driver.c create mode 100644 test/calc.scc create mode 100644 test/calc.slex create mode 100644 test/csharp/SLexSccCalc/Program.cs create mode 100644 test/csharp/SLexSccCalc/SLexSccCalc.csproj diff --git a/.gitignore b/.gitignore index dff9d52..7b486f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ bin/ env.sh .vscode/ +obj/ +scc_generated* +slex_generated* \ No newline at end of file diff --git a/test/c/calc_driver.c b/test/c/calc_driver.c new file mode 100644 index 0000000..4280818 --- /dev/null +++ b/test/c/calc_driver.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include "slex.h" +#include "scc.h" +static double evaluate(scc_syntax_node* node) { + if (!node) return 0.0; + + switch (node->id) { + case scc_id_number_expr: { + if (node->child_count > 0 && node->children[0].type == scc_segment) { + slex_segment* seg = (slex_segment*)node->children[0].data; + return atof(seg->head); + } + return 0.0; + } + case scc_id_group_expr: { + if (node->child_count > 0 && node->children[0].type == scc_node) { + return evaluate((scc_syntax_node*)node->children[0].data); + } + return 0.0; + } + case scc_id_term: + case scc_id_expr: { + if (node->child_count > 0 && node->children[0].type == scc_node) { + return evaluate((scc_syntax_node*)node->children[0].data); + } + return 0.0; + } + case scc_id_add_expr: { + if (node->child_count == 3 && node->children[0].type == scc_node && node->children[2].type == scc_node) { + return evaluate((scc_syntax_node*)node->children[0].data) + evaluate((scc_syntax_node*)node->children[2].data); + } + return 0.0; + } + case scc_id_sub_expr: { + if (node->child_count == 3 && node->children[0].type == scc_node && node->children[2].type == scc_node) { + return evaluate((scc_syntax_node*)node->children[0].data) - evaluate((scc_syntax_node*)node->children[2].data); + } + return 0.0; + } + case scc_id_mul_expr: { + if (node->child_count == 3 && node->children[0].type == scc_node && node->children[2].type == scc_node) { + return evaluate((scc_syntax_node*)node->children[0].data) * evaluate((scc_syntax_node*)node->children[2].data); + } + return 0.0; + } + case scc_id_div_expr: { + if (node->child_count == 3 && node->children[0].type == scc_node && node->children[2].type == scc_node) { + double divisor = evaluate((scc_syntax_node*)node->children[2].data); + if (divisor == 0.0) { + fprintf(stderr, "Error: Division by zero!\n"); + return 0.0; + } + return evaluate((scc_syntax_node*)node->children[0].data) / divisor; + } + return 0.0; + } + default: + return 0.0; + } +} + +static void print_ast(scc_syntax_node* node, int indent) { + if (!node) return; + for (int i = 0; i < indent; i++) printf(" "); + printf("Node: %s (id=%d, child_count=%lld)\n", node->syntax_name, node->id, (long long)node->child_count); + for (uint64_t i = 0; i < node->child_count; i++) { + scc_syntax_node_enclosure* child = &node->children[i]; + if (child->type == scc_node) { + print_ast((scc_syntax_node*)child->data, indent + 1); + } else { + slex_segment* seg = (slex_segment*)child->data; + for (int j = 0; j < indent + 1; j++) printf(" "); + printf("Segment: '%s' (tag=%d, id=%d)\n", seg->head, seg->tag, seg->id); + } + } +} + +int main(int ac, char** av) { + char* input_expr = "3 + 4 * ( 5 - 2 )"; + if(ac==2){ + input_expr=av[1]; + } + printf("Lexing input expression: \"%s\"\n", input_expr); + + slex_segment* lexer_head = NULL; + if (!slex_cstr((char*)input_expr, "input", &lexer_head)) { + fprintf(stderr, "Error: Lexical analysis failed\n"); + return 1; + } + + printf("Parsing lexical segments...\n"); + scc_syntax_node* root = NULL; + if (!scc_parse(lexer_head, &root)) { + fprintf(stderr, "Error: Parsing failed\n"); + slex_free(lexer_head); + return 1; + } + + printf("\nAST Structure:\n"); + print_ast(root, 0); + + double result = evaluate(root); + printf("\nEvaluation Result:\n"); + printf("%s = %g\n", input_expr, result); + + scc_free(root); + slex_free(lexer_head); + return 0; +} diff --git a/test/calc.scc b/test/calc.scc new file mode 100644 index 0000000..f4e778a --- /dev/null +++ b/test/calc.scc @@ -0,0 +1,36 @@ +syntax_ids: +add_expr +sub_expr +mul_expr +div_expr +number_expr +group_expr +term +expr + +rules: + +expr +: expr plus term +=> new_node add_expr $0 $1 $2 +: expr minus term +=> new_node sub_expr $0 $1 $2 +: term +=> new_node expr $0 +; + +term +: term multiply factor +=> new_node mul_expr $0 $1 $2 +: term divide factor +=> new_node div_expr $0 $1 $2 +: factor +=> new_node term $0 +; + +factor +: number +=> new_node number_expr $0 +: lparen expr rparen +=> new_node group_expr $1 +; diff --git a/test/calc.slex b/test/calc.slex new file mode 100644 index 0000000..05469dd --- /dev/null +++ b/test/calc.slex @@ -0,0 +1,42 @@ +rule: + +number [0-9]+ +plus \+ +minus \- +multiply \* +divide \/ +lparen \( +rparen \) +whitespace [ \t\r\n]+ + +mapping: + +number number +plus plus +minus minus +multiply multiply +divide divide +lparen lparen +rparen rparen + +code: + +%c% +%post_processor + if (input->tag == slex_tag_whitespace) { + return slex_skip; + } + *output = input; + return slex_continue; +post_processor% + +%c#% +%post_processor + if (Input.Tag == SegmentTag.whitespace) + { + Output = null!; + return PostProcessResult.Skip; + } + Output = Input; + return PostProcessResult.Continue; +post_processor% diff --git a/test/csharp/SLexSccCalc/Program.cs b/test/csharp/SLexSccCalc/Program.cs new file mode 100644 index 0000000..db9a0e6 --- /dev/null +++ b/test/csharp/SLexSccCalc/Program.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using SLexGenerated; +using SCCGenerated; + +namespace SLexSccCalc +{ + class Program + { + static double Evaluate(SyntaxNode? node) + { + if (node == null) return 0.0; + + switch (node.Id) + { + case SyntaxId.number_expr: + if (node.Children.Count > 0 && node.Children[0].EnclosureType == EnclosureType.Segment) + { + var seg = node.Children[0].Segment; + if (seg != null) return double.Parse(seg.Content); + } + return 0.0; + + case SyntaxId.group_expr: + if (node.Children.Count > 0 && node.Children[0].EnclosureType == EnclosureType.Node) + { + return Evaluate(node.Children[0].Node); + } + return 0.0; + + case SyntaxId.term: + case SyntaxId.expr: + if (node.Children.Count > 0 && node.Children[0].EnclosureType == EnclosureType.Node) + { + return Evaluate(node.Children[0].Node); + } + return 0.0; + + case SyntaxId.add_expr: + if (node.Children.Count == 3 && node.Children[0].EnclosureType == EnclosureType.Node && node.Children[2].EnclosureType == EnclosureType.Node) + { + return Evaluate(node.Children[0].Node) + Evaluate(node.Children[2].Node); + } + return 0.0; + + case SyntaxId.sub_expr: + if (node.Children.Count == 3 && node.Children[0].EnclosureType == EnclosureType.Node && node.Children[2].EnclosureType == EnclosureType.Node) + { + return Evaluate(node.Children[0].Node) - Evaluate(node.Children[2].Node); + } + return 0.0; + + case SyntaxId.mul_expr: + if (node.Children.Count == 3 && node.Children[0].EnclosureType == EnclosureType.Node && node.Children[2].EnclosureType == EnclosureType.Node) + { + return Evaluate(node.Children[0].Node) * Evaluate(node.Children[2].Node); + } + return 0.0; + + case SyntaxId.div_expr: + if (node.Children.Count == 3 && node.Children[0].EnclosureType == EnclosureType.Node && node.Children[2].EnclosureType == EnclosureType.Node) + { + double divisor = Evaluate(node.Children[2].Node); + if (divisor == 0.0) + { + throw new DivideByZeroException("Error: Division by zero!"); + } + return Evaluate(node.Children[0].Node) / divisor; + } + return 0.0; + + default: + return 0.0; + } + } + + static void PrintAST(SyntaxNode? node, int indent) + { + if (node == null) return; + Console.WriteLine(new string(' ', indent * 2) + $"Node: {node.SyntaxName} (id={node.Id}, child_count={node.Children.Count})"); + foreach (var child in node.Children) + { + if (child.EnclosureType == EnclosureType.Node) + { + PrintAST(child.Node, indent + 1); + } + else if (child.Segment != null) + { + Console.WriteLine(new string(' ', (indent + 1) * 2) + $"Segment: '{child.Segment.Content}' (tag={child.Segment.Tag}, id={child.Segment.Id})"); + } + } + } + + static void Main(string[] args) + { + string inputExpr = args.Length > 0 ? string.Join(" ", args) : "3 + 4 * ( 5 - 2 )"; + Console.WriteLine($"Lexing input expression: \"{inputExpr}\""); + + var lexer = new SLexer(); + if (!lexer.SLex(inputExpr, out Segment? lexerHead) || lexerHead == null) + { + Console.Error.WriteLine("Error: Lexical analysis failed"); + return; + } + + Console.WriteLine("Parsing lexical segments..."); + var parser = new SCC(); + if (!parser.Parse(lexerHead, out SyntaxNode? root) || root == null) + { + Console.Error.WriteLine("Error: Parsing failed"); + return; + } + + Console.WriteLine("\nAST Structure:"); + PrintAST(root, 0); + + try + { + double result = Evaluate(root); + Console.WriteLine("\nEvaluation Result:"); + Console.WriteLine($"{inputExpr} = {result}"); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.Message); + } + } + } +} diff --git a/test/csharp/SLexSccCalc/SLexSccCalc.csproj b/test/csharp/SLexSccCalc/SLexSccCalc.csproj new file mode 100644 index 0000000..dfb40ca --- /dev/null +++ b/test/csharp/SLexSccCalc/SLexSccCalc.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + +