Skip to content

Commit 069c929

Browse files
committed
- finished simple calculator via serial port
- added infix postfix unit test - updated readme
1 parent 02d8f4e commit 069c929

File tree

18 files changed

+959
-56
lines changed

18 files changed

+959
-56
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ Note: this repo doesn't support Arduino IDE. Everything must be run from Makefil
1010
+---test/ - all component testers go here, test code uses no os
1111
+---os/ - optional RTOS (currently ChibiOS)
1212
| +---ChibiOS/ -
13-
+---c-project/ - skeleton for C based projects using avr-gcc
13+
+---c-project/ - skeleton for C based projects using avr-gcc
1414
| +---with-os/ -
1515
| +---without-os/ -
16-
+---cpp-project/ - skeleton for C++ based projects using avr-g++
16+
+---cpp-project/ - skeleton for C++ based projects using avr-g++
1717
| +---with-os/ -
1818
| +---without-os/ -
19-
+---doc - documentations
19+
+---doc - documentations about arduino and modules
2020
+---common/ - includes common/unit test makefiles for all projects
2121
| +---utils/ - common protocols such as soft serial, i2c, debugging
2222

@@ -29,9 +29,10 @@ Note: this repo doesn't support Arduino IDE. Everything must be run from Makefil
2929
go to test/ to checkout individual module test code, all code here must be in C except cxxtest headers
3030

3131
# Completed projects
32-
+--c-project/without-os/GPS-breakout-board - i2c console app for arduino, this tool makes i2c debug easier
33-
+--c-project/without-os/i2c-console - gps tracker app that shows parsed distance/angle to lcd and serial port
34-
32+
+--c-project/without-os/GPS-breakout-board - gps tracker app that shows parsed distance/angle to lcd and serial port
33+
+--c-project/without-os/i2c-console - i2c console app for arduino, this tool makes i2c debug easier
34+
+--c-project/without-os/serial-calculator - simple calculator via serial port, supports +-*/() and unsigned number
35+
3536
# Question & Bug fix?
3637
Please email author [email protected]
3738
Or file issue via github
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
/*
2+
* InfixConverter.c
3+
*
4+
* Created on: Jun 2, 2017
5+
* Author: dat
6+
*/
7+
8+
#include "InfixConverter.h"
9+
#include "AVRString.h"
10+
11+
#include <stdio.h>
12+
#include <string.h>
13+
#include <ctype.h>
14+
15+
const char g_infix_supported_ops[] = "+-*/()";
16+
17+
/**
18+
* Compare precedence between a and b
19+
*
20+
* + - < / *
21+
* + = -, * = /
22+
*
23+
* @param a
24+
* @param b
25+
* @return 0 on same
26+
* 1 if a >b
27+
* -1 if a < b
28+
* -2 if error
29+
*/
30+
static int8_t comparePriority(char a, char b)
31+
{
32+
int8_t retVal = -2;
33+
34+
uint8_t a_pm = (a == '+') || (a == '-');
35+
uint8_t b_pm = (b == '+') || (b == '-');
36+
uint8_t a_md = (a == '*') || (a == '/');
37+
uint8_t b_md = (b == '*') || (b == '/');
38+
39+
if (a_pm && b_pm)
40+
{
41+
retVal = 0;
42+
}
43+
else if (a_pm && b_md)
44+
{
45+
retVal = -1;
46+
}
47+
else if (a_md && b_pm)
48+
{
49+
retVal = 1;
50+
}
51+
52+
return retVal;
53+
}
54+
55+
/**
56+
* append " %c" to str
57+
*
58+
* @param str
59+
* @param c
60+
*/
61+
static void stringPushBackChar(char * str, char c)
62+
{
63+
sprintf(str, "%s %c", str, c);
64+
}
65+
66+
/**
67+
* appen " %u" to str
68+
*
69+
* @param str
70+
* @param num
71+
*/
72+
static void stringPushBackInt(char * str, long num)
73+
{
74+
sprintf(str, "%s %ld", str, num);
75+
}
76+
77+
/*
78+
* Make sure buffer is large enough
79+
*
80+
* return 2 on unsupported char
81+
* 0 on success
82+
*/
83+
uint8_t addSpaces(char * buffer)
84+
{
85+
// count special chars
86+
uint8_t i;
87+
char buffer2[STRING_MAXLEN];
88+
uint8_t index2 = 0;
89+
for (i = 0; i < strlen(buffer); ++i)
90+
{
91+
if (strchr(g_infix_supported_ops, buffer[i]))
92+
{
93+
buffer2[index2++] = ' ';
94+
buffer2[index2++] = buffer[i];
95+
buffer2[index2++] = ' ';
96+
}
97+
else
98+
{
99+
buffer2[index2++] = buffer[i];
100+
}
101+
}
102+
buffer2[index2] = '\0';
103+
AVRStrinStripExtraSpace(buffer2);
104+
AVRStringTrimWhiteSpace(buffer2);
105+
106+
strcpy(buffer, buffer2);
107+
108+
return 0;
109+
}
110+
111+
/**
112+
* get top of op_stack
113+
*
114+
* @param stack - op stack
115+
* @return
116+
*/
117+
static char getStackTop(const char * stack)
118+
{
119+
if (strlen(stack))
120+
{
121+
return stack[strlen(stack) - 1];
122+
}
123+
124+
return '\0';
125+
}
126+
127+
/**
128+
* pop the top of op_stack
129+
*
130+
* @param stack
131+
*/
132+
static char popStack(char * stack)
133+
{
134+
char retVal = getStackTop(stack);
135+
136+
if (strlen(stack))
137+
{
138+
stack[strlen(stack) - 1] = '\0';
139+
}
140+
141+
return retVal;
142+
}
143+
144+
/**
145+
* push c to top of op stack
146+
*
147+
* @param stack
148+
* @param c
149+
*/
150+
void pushStack(char * stack, char c)
151+
{
152+
stack[strlen(stack) + 1] = '\0';
153+
stack[strlen(stack)] = c;
154+
}
155+
156+
/*
157+
* Algorithm:
158+
*
159+
* 1. Print operands as they arrive.
160+
* 2. If the stack is empty or contains a left parenthesis on top, push the incoming operator onto the stack.
161+
* 3. If the incoming symbol is a left parenthesis, push it on the stack.
162+
* 4. If the incoming symbol is a right parenthesis, pop the stack and print the operators until you see a left parenthesis. Discard the pair of parentheses.
163+
* 5. If the incoming symbol has higher precedence than the top of the stack, push it on the stack.
164+
* 6. If the incoming symbol has equal or lower precedence than the symbol on the top of the stack, pop the stack and print the top operator. Then test the incoming operator against the new top of stack.
165+
* 7. At the end of the expression, pop and print all operators on the stack. (No parentheses should remain.)
166+
*/
167+
uint8_t InfixConverterConvertString(const char * input, char * output)
168+
{
169+
uint8_t retVal = 0;
170+
171+
// refine input & cpy to output
172+
output[0] = '\0';
173+
char output_tmp[STRING_MAXLEN];
174+
strcpy(output_tmp, input);
175+
AVRStringRemoveChar(output_tmp, ' '); // rm all spaces
176+
177+
// test for unsupported char
178+
unsigned i;
179+
for (i = 0; i < strlen(output_tmp); ++i)
180+
{
181+
if (!strchr(g_infix_supported_ops, output_tmp[i])
182+
&& !isdigit(output_tmp[i]))
183+
{
184+
retVal = 2;
185+
}
186+
}
187+
if (retVal)
188+
return retVal;
189+
190+
retVal = addSpaces(output_tmp);
191+
if (retVal)
192+
return retVal;
193+
194+
// parse each token, can be "123" or "+"
195+
char op_stack[20] =
196+
{ '\0' };
197+
char * token = strtok(output_tmp, " ");
198+
199+
while (token)
200+
{
201+
char incomingSymbol = token[0];
202+
203+
// 1. Print operands as they arrive.
204+
long num;
205+
if (sscanf(token, "%ld", &num) == 1)
206+
{
207+
stringPushBackInt(output, num);
208+
}
209+
// error checking, if token isn't number, it must exist in g_infix_supported_ops
210+
else if (!strchr(g_infix_supported_ops, incomingSymbol))
211+
{
212+
retVal = 2;
213+
}
214+
215+
// 2. If the stack is empty or contains a left parenthesis on top, push the incoming operator onto the stack.
216+
else if (!getStackTop(op_stack) || getStackTop(op_stack) == '(')
217+
{
218+
pushStack(op_stack, incomingSymbol);
219+
}
220+
221+
// 3. If the incoming symbol is a left parenthesis, push it on the stack.
222+
else if (incomingSymbol == '(')
223+
{
224+
pushStack(op_stack, incomingSymbol);
225+
}
226+
227+
// 4. If the incoming symbol is a right parenthesis, pop the stack and print the operators
228+
// until you see a left parenthesis. Discard the pair of parentheses.
229+
else if (incomingSymbol == ')')
230+
{
231+
// ensure ( exists in stack
232+
if (strchr(op_stack, '('))
233+
{
234+
while (getStackTop(op_stack) != '(')
235+
{
236+
stringPushBackChar(output, popStack(op_stack));
237+
}
238+
239+
// discard '('
240+
popStack(op_stack);
241+
}
242+
else
243+
{
244+
retVal = 1;
245+
}
246+
}
247+
248+
// 5. If the incoming symbol has higher precedence than the top of the stack, push it on the stack.
249+
else if (comparePriority(incomingSymbol, getStackTop(op_stack)) == 1)
250+
{
251+
pushStack(op_stack, incomingSymbol);
252+
}
253+
254+
// 6. If the incoming symbol has equal or lower precedence than the symbol on the top of the stack,
255+
// pop the stack and print the top operator. Then test the incoming operator against the new top of stack.
256+
else if (comparePriority(incomingSymbol, getStackTop(op_stack)) <=0)
257+
{
258+
stringPushBackChar(output, popStack(op_stack));
259+
260+
while ( strlen(op_stack) && comparePriority(incomingSymbol, getStackTop(op_stack)) <= 0)
261+
{
262+
stringPushBackChar(output, popStack(op_stack));
263+
}
264+
265+
pushStack(op_stack, incomingSymbol);
266+
}
267+
268+
else
269+
{
270+
retVal = 1;
271+
}
272+
273+
if (retVal)
274+
break;
275+
276+
token = strtok(NULL, " ");
277+
}
278+
279+
// push the rest of op stack
280+
if (strlen(op_stack))
281+
{
282+
if (!strchr(op_stack, '(')) // check if op stack still contains '('
283+
{
284+
while (getStackTop(op_stack))
285+
{
286+
stringPushBackChar(output, popStack(op_stack));
287+
}
288+
}
289+
else
290+
{
291+
retVal = 1;
292+
}
293+
}
294+
295+
if (retVal)
296+
{
297+
output[0] = '\0';
298+
}
299+
else
300+
{
301+
strcpy(output, AVRStringTrimWhiteSpace(output));
302+
}
303+
304+
return retVal;
305+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* InfixConverter.h
3+
*
4+
* Created on: Jun 2, 2017
5+
* Author: dat
6+
*/
7+
8+
#ifndef SERIAL_CALCULATOR_INFIXCONVERTER_H_
9+
#define SERIAL_CALCULATOR_INFIXCONVERTER_H_
10+
11+
#include <inttypes.h>
12+
13+
/**
14+
* convert from infix to postfix
15+
* Support add subtract mult div ()
16+
* Currently support unsigned input
17+
*
18+
* WIP : +
19+
*
20+
* @param input - eg. "1+2", "1 * (2 + 3)"
21+
* @param output - eg. "1 2 +", "1 2 3 + *"
22+
* @return - 0 on success
23+
* - 1 on error format
24+
* - 2 on unsupported char
25+
*/
26+
uint8_t InfixConverterConvertString(const char * input, char * output);
27+
28+
#endif /* SERIAL_CALCULATOR_INFIXCONVERTER_H_ */
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# to program to arduino, type make flash
2+
CC=avr-gcc
3+
CFLAGS = -Wextra
4+
DEBUG = yes
5+
FLOAT_SUPPORT = no # support printf, sscanf floats
6+
UTILS_SUPPORT = yes # extra utils such as soft serial, i2c, serial debug
7+
8+
# required
9+
TOP = ../../../
10+
SOURCES = $(wildcard *.c)
11+
INCLUDES = ./
12+
13+
# unit test files
14+
# remember to put cxx test headers in test/*Test.h
15+
TEST_SOURCES = InfixConverter.c PostfixCalculator.c $(UTILS_DIR)/AVRString.c
16+
TEST_DEBUG = no
17+
TEST_CFLAGS = -std=gnu++0x
18+
19+
# specify your preferred .mk file
20+
include $(TOP)/common/without-os.mk
21+
include $(TOP)/common/unittest.mk
22+

0 commit comments

Comments
 (0)