1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7%{ /* -*- C++ -*- */
8# include <cerrno>
9# include <climits>
10# include <cstdlib>
11# include <cstring>
12# include <string>
13# include "pio_assembler.h"
14# include "parser.hpp"
15
16#ifdef _MSC_VER
17#pragma warning(disable : 4996) // fopen
18#endif
19
20%}
21
22%option noyywrap nounput noinput batch debug never-interactive case-insensitive noline
23
24%{
25  yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc);
26  yy::parser::symbol_type make_FLOAT(const std::string &s, const yy::parser::location_type& loc);
27  yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc);
28  yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc);
29%}
30
31blank         [ \t\r]
32whitesp       {blank}+
33
34comment       (";"|"//")[^\n]*
35
36digit         [0-9]
37id            [a-zA-Z_][a-zA-Z0-9_]*
38
39binary        "0b"[01]+
40int           {digit}+
41float         {digit}*\.{digit}+
42hex	          "0x"[0-9a-fA-F]+
43directive     \.{id}
44
45output_fmt    [^%\n]+
46
47%{
48  // Code run each time a pattern is matched.
49  # define YY_USER_ACTION  loc.columns (yyleng);
50%}
51
52%x code_block
53%x c_comment
54%x lang_opt
55
56%%
57        std::string code_block_contents;
58        yy::location code_block_start;
59%{
60  // A handy shortcut to the location held by the pio_assembler.
61  yy::location& loc = pioasm.location;
62  // Code run each time yylex is called.
63  loc.step();
64%}
65
66{blank}+                            loc.step();
67\n+                                 { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline); }
68
69"%"{blank}*{output_fmt}{blank}*"{"  {
70                                        BEGIN(code_block);
71                                        code_block_contents = "";
72                                        code_block_start = loc;
73                                        std::string tmp(yytext);
74                                        tmp = tmp.substr(1, tmp.length() - 2);
75                                        tmp = tmp.erase(0, tmp.find_first_not_of(" \t"));
76                                        tmp = tmp.erase(tmp.find_last_not_of(" \t") + 1);
77                                        return yy::parser::make_CODE_BLOCK_START( tmp, loc);
78                                    }
79<code_block>{
80    {blank}+                        loc.step();
81    \n+                             { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
82    "%}"{blank}*                    { BEGIN(INITIAL); auto loc2 = loc; loc2.begin = code_block_start.begin; return yy::parser::make_CODE_BLOCK_CONTENTS(code_block_contents, loc2); }
83    .*                              { code_block_contents += std::string(yytext) + "\n"; }
84}
85
86<c_comment>{
87    {blank}+                        loc.step();
88    "*/"                            { BEGIN(INITIAL); }
89    "*"                             { }
90    [^\n\*]*                        { }
91    \n+                             { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
92}
93
94<lang_opt>{
95\"[^\n]*\"                          return yy::parser::make_STRING(yytext, loc);
96{blank}+                            loc.step();
97"="		                            return yy::parser::make_ASSIGN(loc);
98{int}                               return make_INT(yytext, loc);
99{hex}                               return make_HEX(yytext, loc);
100{binary}                            return make_BINARY(yytext, loc);
101[^ \t\n\"=]+                         return yy::parser::make_NON_WS(yytext, loc);
102\n+                                 { BEGIN(INITIAL); auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline);  }
103.                                   { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
104}
105
106"/*"                                { BEGIN(c_comment); }
107","	                                return yy::parser::make_COMMA(loc);
108"::"                                return yy::parser::make_REVERSE(loc);
109":"	                                return yy::parser::make_COLON(loc);
110"["                                 return yy::parser::make_LBRACKET(loc);
111"]"                                 return yy::parser::make_RBRACKET(loc);
112"("                                 return yy::parser::make_LPAREN(loc);
113")"                                 return yy::parser::make_RPAREN(loc);
114"+"                                 return yy::parser::make_PLUS(loc);
115"--"                                return yy::parser::make_POST_DECREMENT(loc);
116"−−"                                return yy::parser::make_POST_DECREMENT(loc);
117"-"                                 return yy::parser::make_MINUS(loc);
118"*"                                 return yy::parser::make_MULTIPLY(loc);
119"/"                                 return yy::parser::make_DIVIDE(loc);
120"|"                                 return yy::parser::make_OR(loc);
121"&"                                 return yy::parser::make_AND(loc);
122">>"                                return yy::parser::make_SHR(loc);
123"<<"                                return yy::parser::make_SHL(loc);
124"^"                                 return yy::parser::make_XOR(loc);
125"!="		                        return yy::parser::make_NOT_EQUAL(loc);
126"!"			                        return yy::parser::make_NOT(loc);
127"~"			                        return yy::parser::make_NOT(loc);
128"<"                                 return yy::parser::make_LESSTHAN(loc);
129
130".program"		                    return yy::parser::make_PROGRAM(loc);
131".wrap_target"	                    return yy::parser::make_WRAP_TARGET(loc);
132".wrap"			                    return yy::parser::make_WRAP(loc);
133".word"			                    return yy::parser::make_WORD(loc);
134".define"		                    return yy::parser::make_DEFINE(loc);
135".side_set"		                    return yy::parser::make_SIDE_SET(loc);
136".origin"		                    return yy::parser::make_ORIGIN(loc);
137".lang_opt"         	            { BEGIN(lang_opt); return yy::parser::make_LANG_OPT(loc); }
138".pio_version"                      return yy::parser::make_PIO_VERSION(loc);
139".clock_div"                        return yy::parser::make_CLOCK_DIV(loc);
140".fifo"                             return yy::parser::make_FIFO(loc);
141".mov_status"                       return yy::parser::make_MOV_STATUS(loc);
142".set"                              return yy::parser::make_DOT_SET(loc);
143".out"                              return yy::parser::make_DOT_OUT(loc);
144".in"                               return yy::parser::make_DOT_IN(loc);
145
146{directive}                         return yy::parser::make_UNKNOWN_DIRECTIVE(yytext, loc);
147
148"JMP"			                    return yy::parser::make_JMP(loc);
149"WAIT"			                    return yy::parser::make_WAIT(loc);
150"IN"			                    return yy::parser::make_IN(loc);
151"OUT"			                    return yy::parser::make_OUT(loc);
152"PUSH"			                    return yy::parser::make_PUSH(loc);
153"PULL"			                    return yy::parser::make_PULL(loc);
154"MOV"			                    return yy::parser::make_MOV(loc);
155"IRQ"			                    return yy::parser::make_IRQ(loc);
156"SET"			                    return yy::parser::make_SET(loc);
157"NOP"			                    return yy::parser::make_NOP(loc);
158
159"PUBLIC"		                    return yy::parser::make_PUBLIC(loc);
160
161"OPTIONAL"		                    return yy::parser::make_OPTIONAL(loc);
162"OPT"			                    return yy::parser::make_OPTIONAL(loc);
163"SIDE"			                    return yy::parser::make_SIDE(loc);
164"SIDESET"	                        return yy::parser::make_SIDE(loc);
165"SIDE_SET"   	                    return yy::parser::make_SIDE(loc);
166"PIN"			                    return yy::parser::make_PIN(loc);
167"GPIO"			                    return yy::parser::make_GPIO(loc);
168"OSRE"			                    return yy::parser::make_OSRE(loc);
169
170"PINS"			                    return yy::parser::make_PINS(loc);
171"NULL"			                    return yy::parser::make_NULL(loc);
172"PINDIRS"		                    return yy::parser::make_PINDIRS(loc);
173"X"	    		                    return yy::parser::make_X(loc);
174"Y"		    	                    return yy::parser::make_Y(loc);
175"PC"			                    return yy::parser::make_PC(loc);
176"EXEC"			                    return yy::parser::make_EXEC(loc);
177"ISR"			                    return yy::parser::make_ISR(loc);
178"OSR"			                    return yy::parser::make_OSR(loc);
179"STATUS"		                    return yy::parser::make_STATUS(loc);
180
181"BLOCK"			                    return yy::parser::make_BLOCK(loc);
182"NOBLOCK"		                    return yy::parser::make_NOBLOCK(loc);
183"IFFULL"		                    return yy::parser::make_IFFULL(loc);
184"IFEMPTY"		                    return yy::parser::make_IFEMPTY(loc);
185"REL"			                    return yy::parser::make_REL(loc);
186
187"CLEAR"			                    return yy::parser::make_CLEAR(loc);
188"NOWAIT"		                    return yy::parser::make_NOWAIT(loc);
189"JMPPIN"                            return yy::parser::make_JMPPIN(loc);
190"NEXT"                              return yy::parser::make_NEXT(loc);
191"PREV"                              return yy::parser::make_PREV(loc);
192
193"TXRX"                              return yy::parser::make_TXRX(loc);
194"TX"                                return yy::parser::make_TX(loc);
195"RX"                                return yy::parser::make_RX(loc);
196"TXPUT"                             return yy::parser::make_TXPUT(loc);
197"TXGET"                             return yy::parser::make_TXGET(loc);
198"PUTGET"                            return yy::parser::make_PUTGET(loc);
199
200"ONE"                               return yy::parser::make_INT(1, loc);
201"ZERO"                              return yy::parser::make_INT(0, loc);
202
203"RP2040"                            return yy::parser::make_RP2040(loc);
204"RP2350"                            return yy::parser::make_RP2350(loc);
205"RXFIFO"                            return yy::parser::make_RXFIFO(loc);
206"TXFIFO"                            return yy::parser::make_TXFIFO(loc);
207
208"LEFT"                              return yy::parser::make_LEFT(loc);
209"RIGHT"                             return yy::parser::make_RIGHT(loc);
210"AUTO"                              return yy::parser::make_AUTO(loc);
211"MANUAL"                            return yy::parser::make_MANUAL(loc);
212<<EOF>>                             return yy::parser::make_END(loc);
213
214{int}                               return make_INT(yytext, loc);
215{float}                             return make_FLOAT(yytext, loc);
216{hex}                               return make_HEX(yytext, loc);
217{binary}                            return make_BINARY(yytext, loc);
218
219{id}                                return yy::parser::make_ID(yytext, loc);
220
221{comment}                           { }
222
223.                                   { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
224
225%%
226
227yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc)
228{
229  errno = 0;
230  long n = strtol (s.c_str(), NULL, 10);
231  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
232    throw yy::parser::syntax_error (loc, "integer is out of range: " + s);
233  return yy::parser::make_INT((int) n, loc);
234}
235
236yy::parser::symbol_type make_FLOAT(const std::string &s, const yy::parser::location_type& loc)
237{
238  errno = 0;
239  float n = strtof (s.c_str(), NULL);
240  return yy::parser::make_FLOAT(n, loc);
241}
242
243yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc)
244{
245  errno = 0;
246  long n = strtol (s.c_str() + 2, NULL, 16);
247  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
248    throw yy::parser::syntax_error (loc, "hex is out of range: " + s);
249  return yy::parser::make_INT((int) n, loc);
250}
251
252yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc)
253{
254  errno = 0;
255  long n = strtol (s.c_str()+2, NULL, 2);
256  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
257    throw yy::parser::syntax_error (loc, "binary is out of range: " + s);
258  return yy::parser::make_INT((int) n, loc);
259}
260
261void pio_assembler::scan_begin ()
262{
263  yy_flex_debug = false;
264  if (source.empty () || source == "-")
265    yyin = stdin;
266  else if (!(yyin = fopen (source.c_str (), "r")))
267    {
268      std::cerr << "cannot open " << source << ": " << strerror(errno) << '\n';
269      exit (EXIT_FAILURE);
270    }
271}
272
273void pio_assembler::scan_end ()
274{
275  fclose (yyin);
276}
277