这听起来像是家庭作业/学习练习,所以我将解释计划/设计这样的程序的过程。然后我会给你一个实现它的方法的示例。
编写此类程序的一种方法是构建一对状态机:
- 初始状态/空白状态
- 从状态1:已经看到
/*
序列;保持这种状态直到*/
;返回 1
- 从状态1:见过
"
性格;保持这种状态直到\
或"
;返回 1
- 从状态1:已经看到
//
序列;保持这种状态直到换行;返回 1
- 从状态1:看到一封信;保持这种状态捕获直到非字母和非数字
- 从状态 1:看到其他任何东西,保持这种状态直到换行。
- 从状态 3:已经看到
\
提取下一个字符,然后返回状态 3
一旦离开状态 5:
- 如果这个词是
switch
我们将进入我们的另一个状态机(如下)。
- 如果我们没有看到
:
,则返回状态 1
- 否则我们的话就是一个标签;用它做任何你想做的事情并返回到状态 1。
但是,当我们看到switch
时,由于紧随其后的表达式,我们需要另一个状态表:
- 初始状态/空白
- 从状态 1:
(
在开始之后switch
- 从状态 1:所有其他字符生成错误。
- 从状态 2:
/*
并被"
视为状态机 1(上图)
- 从状态 2:
)
看到
- 从状态 5:
{
- 增加嵌套级别并立即进入状态 7。
- 空白/状态
- 从状态 7 开始:
}
- 在进入该状态时降低嵌套级别;如果嵌套级别为零,则在第一个状态机中返回状态 1,否则保持状态 7。
- 从状态 7:看到一封信;保持这种状态捕获直到非字母或
:
- 从状态 7:看到其他任何东西,保持在这个状态直到
;
或}
分别移动到状态 7 和 8。
一旦离开状态 9:
- 如果我们没有看到
:
然后转到状态 10
- 否则如果单词 as
default
则进入状态 10
- 否则这是一个标签;用它做任何你想做的事情,然后进入状态 10
下一步是为每个状态编写一个函数。给状态编号并给函数编号可能会有所帮助。他们对“字符”或“字符序列”的操作方式通常是我早期写的一些低级的东西:
int c;
int next() { c=getchar(); return c; }
typedef int (*state)();
例如,上面的状态 2 和 3 可以写成:
state state2() { if(c == '*' && next() == '/') return state1; return state2; }
state state3() { if(c == '"') return state1; if (c == '\\') return state7; }
算出其余的州应该很容易。状态 5 将有一个缓冲区,您正在填充该缓冲区以“读取”单词:
char word[600];
int ptr;
state state5() {
ptr = 0;
while(isdigit(c) || isalpha(c)) {
word[ptr] = c;
ptr++;
if(ptr==600)abort();
next();
}
/* now leaving state 5 */
}
完成此操作后,您可以编写驱动程序:
void statemachine1() {
state x = state1;
while(c != -1) x = x();
}
如果您犯了错误,拥有一些调试工具会很有帮助。一个好的方法是在数组中标记每个状态:
state statelist1[] = { state1, state2, state3, state4, state5, state6, state7 };
int statenumber(state x) {
int i, n = sizeof(statelist1) / sizeof(state);
while(n-->0) if(x == statelist1[n]) return n;
abort();
}
这在调试时会很有帮助;我可能会插入:
printf("state = %d, char = %02x (%c)\n", statenumber(x), c,c);
进入驱动程序循环,如下所示:
printf("char = %02x (%c)\n", c,c);
在我追踪机器的各个部分。这将在我的脑海中更牢固地建立状态机地图,并且当我沿着我的测试程序跟踪它时,它将更容易验证所有状态。
如果事情开始变得困难,我将修改next()
以跟踪当前行:
int line = 1;
int next() { c=getchar(); if(c == '\n') line++; return c; }
这样我也可以在我的printf()
陈述中使用它。
一旦我对一切正常工作感到满意,我将删除调试代码。
祝你好运!