3. 解析多地址指令

解析多地址指令 学习卡片

任务目标

扩展单地址指令解析器,使其能够解析一条G代码行中出现的多个地址字(如 N10 G01 X10.5 Y-2.5 Z0 F300),并将每个地址字解析为独立的指令对象,按出现顺序输出。

实现方案

在词法层面将输入行按空白分割为序列,每个匹配 地址字母 + 数值 模式。解析器遍历序列,对每个调用地址解析函数,生成 Word 对象列表。数值支持整数、小数、正负号,不依赖正则表达式,使用手写状态机完成数值提取。

关键完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef struct {
    char addr;
    double value;
} Word;

// 手写数值解析:支持 12、-3.5、+0.25、.5 等格式
const char* parse_number(const char* p, double* out) {
    double int_part = 0.0, frac_part = 0.0;
    int sign = 1, has_digit = 0;
    if (*p == '+' || *p == '-') {
        if (*p == '-') sign = -1;
        p++;
    }
    while (isdigit((unsigned char)*p)) {
        int_part = int_part * 10.0 + (*p - '0');
        has_digit = 1;
        p++;
    }
    if (*p == '.') {
        p++;
        double scale = 0.1;
        while (isdigit((unsigned char)*p)) {
            frac_part += (*p - '0') * scale;
            scale *= 0.1;
            has_digit = 1;
            p++;
        }
    }
    if (!has_digit) return NULL;  // 无有效数字
    *out = sign * (int_part + frac_part);
    return p;
}

// 解析单个地址字,成功返回1,失败返回0
int parse_word(const char* , Word* w) {
    if (!isalpha((unsigned char)[0])) return 0;
    w->addr = [0];
    double val;
    const char* end = parse_number( + 1, &val);
    if (!end || *end != '\0') return 0;  // 数值后还有多余字符
    w->value = val;
    return 1;
}

// 解析整行,返回解析成功的字数,-1表示行内有非法
int parse_line(const char* line, Word* words, int max_words) {
    char buf[256];
    strncpy(buf, line, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';

    int count = 0;
    char* saveptr;
    char*  = strtok_r(buf, " \t\r\n", &saveptr);
    while ( && count < max_words) {
        if (!parse_word(, &words[count])) return -1;
        count++;
         = strtok_r(NULL, " \t\r\n", &saveptr);
    }
    return count;
}

int main(void) {
    const char* test_line = "N10 G01 X10.5 Y-2.5 Z0 F300";
    Word words[16];
    int n = parse_line(test_line, words, 16);
    if (n < 0) {
        printf("解析失败:存在非法\n");
        return 1;
    }
    for (int i = 0; i < n; i++) {
        printf("%c %.4f\n", words[i].addr, words[i].value);
    }
    return 0;
}

编译与运行

gcc -o parser parser.c
./parser

验证方法

程序以硬编码测试行 N10 G01 X10.5 Y-2.5 Z0 F300 作为输入。编译运行后,观察标准输出是否依次打印6行:N 10.0000、G 1.0000、X 10.5000、Y -2.5000、Z 0.0000、F 300.0000。若输出与此顺序和数值一致,说明多地址解析正确。

边界行为说明

  • 若某不是以字母开头(如 123),或字母后无有效数字(如 X、Xabc),parse_line 返回 -1,主程序报告解析失败。
  • 地址字母大小写敏感,x10 与 X10 视为不同地址字。
  • 数值解析器不依赖 strtod,可处理 +.5、-0.25 等格式;但指数形式(如 1e3)不被支持,会判定为非法。