Added a calculator test program.

This commit is contained in:
Creeper Lv
2026-05-26 19:34:04 +10:00
parent 2d90caa285
commit 89354af95e
6 changed files with 331 additions and 0 deletions
+3
View File
@@ -1,3 +1,6 @@
bin/
env.sh
.vscode/
obj/
scc_generated*
slex_generated*
+111
View File
@@ -0,0 +1,111 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}
+36
View File
@@ -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
;
+42
View File
@@ -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%
+129
View File
@@ -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);
}
}
}
}
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>