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_HEX(const std::string &s, const yy::parser::location_type& loc);
27  yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc);
28%}
29
30blank         [ \t\r]
31whitesp       {blank}+
32
33comment       (";"|"//")[^\n]*
34
35digit         [0-9]
36id            [a-zA-Z_][a-zA-Z0-9_]*
37
38binary        "0b"[01]+
39int           {digit}+
40hex	          "0x"[0-9a-fA-F]+
41directive     \.{id}
42
43output_fmt    [^%\n]+
44
45%{
46  // Code run each time a pattern is matched.
47  # define YY_USER_ACTION  loc.columns (yyleng);
48%}
49
50%x code_block
51%x c_comment
52%x lang_opt
53
54%%
55        std::string code_block_contents;
56        yy::location code_block_start;
57%{
58  // A handy shortcut to the location held by the pio_assembler.
59  yy::location& loc = pioasm.location;
60  // Code run each time yylex is called.
61  loc.step();
62%}
63
64{blank}+                            loc.step();
65\n+                                 { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); return yy::parser::make_NEWLINE(loc_newline); }
66
67"%"{blank}*{output_fmt}{blank}*"{"  {
68                                        BEGIN(code_block);
69                                        code_block_contents = "";
70                                        code_block_start = loc;
71                                        std::string tmp(yytext);
72                                        tmp = tmp.substr(1, tmp.length() - 2);
73                                        tmp = tmp.erase(0, tmp.find_first_not_of(" \t"));
74                                        tmp = tmp.erase(tmp.find_last_not_of(" \t") + 1);
75                                        return yy::parser::make_CODE_BLOCK_START( tmp, loc);
76                                    }
77<code_block>{
78    {blank}+                        loc.step();
79    \n+                             { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
80    "%}"{blank}*                    { BEGIN(INITIAL); auto loc2 = loc; loc2.begin = code_block_start.begin; return yy::parser::make_CODE_BLOCK_CONTENTS(code_block_contents, loc2); }
81    .*                              { code_block_contents += std::string(yytext) + "\n"; }
82}
83
84<c_comment>{
85    {blank}+                        loc.step();
86    "*/"                            { BEGIN(INITIAL); }
87    "*"                             { }
88    [^\n\*]*                        { }
89    \n+                             { auto loc_newline = loc; loc_newline.end = loc_newline.begin; loc.lines(yyleng); loc.step(); }
90}
91
92<lang_opt>{
93\"[^\n]*\"                          return yy::parser::make_STRING(yytext, loc);
94{blank}+                            loc.step();
95"="		                            return yy::parser::make_EQUAL(loc);
96{int}                               return make_INT(yytext, loc);
97{hex}                               return make_HEX(yytext, loc);
98{binary}                            return make_BINARY(yytext, loc);
99[^ \t\n\"=]+                         return yy::parser::make_NON_WS(yytext, loc);
100\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);  }
101.                                   { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
102}
103
104"/*"                                { BEGIN(c_comment); }
105","	                                return yy::parser::make_COMMA(loc);
106"::"                                return yy::parser::make_REVERSE(loc);
107":"	                                return yy::parser::make_COLON(loc);
108"["                                 return yy::parser::make_LBRACKET(loc);
109"]"                                 return yy::parser::make_RBRACKET(loc);
110"("                                 return yy::parser::make_LPAREN(loc);
111")"                                 return yy::parser::make_RPAREN(loc);
112"+"                                 return yy::parser::make_PLUS(loc);
113"--"                                return yy::parser::make_POST_DECREMENT(loc);
114"−−"                                return yy::parser::make_POST_DECREMENT(loc);
115"-"                                 return yy::parser::make_MINUS(loc);
116"*"                                 return yy::parser::make_MULTIPLY(loc);
117"/"                                 return yy::parser::make_DIVIDE(loc);
118"|"                                 return yy::parser::make_OR(loc);
119"&"                                 return yy::parser::make_AND(loc);
120"^"                                 return yy::parser::make_XOR(loc);
121"!="		                        return yy::parser::make_NOT_EQUAL(loc);
122"!"			                        return yy::parser::make_NOT(loc);
123"~"			                        return yy::parser::make_NOT(loc);
124
125".program"		                    return yy::parser::make_PROGRAM(loc);
126".wrap_target"	                    return yy::parser::make_WRAP_TARGET(loc);
127".wrap"			                    return yy::parser::make_WRAP(loc);
128".word"			                    return yy::parser::make_WORD(loc);
129".define"		                    return yy::parser::make_DEFINE(loc);
130".side_set"		                    return yy::parser::make_SIDE_SET(loc);
131".origin"		                    return yy::parser::make_ORIGIN(loc);
132".lang_opt"         	            { BEGIN(lang_opt); return yy::parser::make_LANG_OPT(loc); }
133{directive}                         return yy::parser::make_UNKNOWN_DIRECTIVE(yytext, loc);
134
135"JMP"			                    return yy::parser::make_JMP(loc);
136"WAIT"			                    return yy::parser::make_WAIT(loc);
137"IN"			                    return yy::parser::make_IN(loc);
138"OUT"			                    return yy::parser::make_OUT(loc);
139"PUSH"			                    return yy::parser::make_PUSH(loc);
140"PULL"			                    return yy::parser::make_PULL(loc);
141"MOV"			                    return yy::parser::make_MOV(loc);
142"IRQ"			                    return yy::parser::make_IRQ(loc);
143"SET"			                    return yy::parser::make_SET(loc);
144"NOP"			                    return yy::parser::make_NOP(loc);
145
146"PUBLIC"		                    return yy::parser::make_PUBLIC(loc);
147
148"OPTIONAL"		                    return yy::parser::make_OPTIONAL(loc);
149"OPT"			                    return yy::parser::make_OPTIONAL(loc);
150"SIDE"			                    return yy::parser::make_SIDE(loc);
151"SIDESET"	                        return yy::parser::make_SIDE(loc);
152"SIDE_SET"   	                    return yy::parser::make_SIDE(loc);
153"PIN"			                    return yy::parser::make_PIN(loc);
154"GPIO"			                    return yy::parser::make_GPIO(loc);
155"OSRE"			                    return yy::parser::make_OSRE(loc);
156
157"PINS"			                    return yy::parser::make_PINS(loc);
158"NULL"			                    return yy::parser::make_NULL(loc);
159"PINDIRS"		                    return yy::parser::make_PINDIRS(loc);
160"X"	    		                    return yy::parser::make_X(loc);
161"Y"		    	                    return yy::parser::make_Y(loc);
162"PC"			                    return yy::parser::make_PC(loc);
163"EXEC"			                    return yy::parser::make_EXEC(loc);
164"ISR"			                    return yy::parser::make_ISR(loc);
165"OSR"			                    return yy::parser::make_OSR(loc);
166"STATUS"		                    return yy::parser::make_STATUS(loc);
167
168"BLOCK"			                    return yy::parser::make_BLOCK(loc);
169"NOBLOCK"		                    return yy::parser::make_NOBLOCK(loc);
170"IFFULL"		                    return yy::parser::make_IFFULL(loc);
171"IFEMPTY"		                    return yy::parser::make_IFEMPTY(loc);
172"REL"			                    return yy::parser::make_REL(loc);
173
174"CLEAR"			                    return yy::parser::make_CLEAR(loc);
175"NOWAIT"		                    return yy::parser::make_NOWAIT(loc);
176
177"ONE"                               return yy::parser::make_INT(1, loc);
178"ZERO"                              return yy::parser::make_INT(0, loc);
179
180<<EOF>>                             return yy::parser::make_END(loc);
181
182{int}                               return make_INT(yytext, loc);
183{hex}                               return make_HEX(yytext, loc);
184{binary}                            return make_BINARY(yytext, loc);
185
186{id}                                return yy::parser::make_ID(yytext, loc);
187
188{comment}                           { }
189
190.                                   { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext)); }
191
192%%
193
194yy::parser::symbol_type make_INT(const std::string &s, const yy::parser::location_type& loc)
195{
196  errno = 0;
197  long n = strtol (s.c_str(), NULL, 10);
198  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
199    throw yy::parser::syntax_error (loc, "integer is out of range: " + s);
200  return yy::parser::make_INT((int) n, loc);
201}
202
203yy::parser::symbol_type make_HEX(const std::string &s, const yy::parser::location_type& loc)
204{
205  errno = 0;
206  long n = strtol (s.c_str() + 2, NULL, 16);
207  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
208    throw yy::parser::syntax_error (loc, "hex is out of range: " + s);
209  return yy::parser::make_INT((int) n, loc);
210}
211
212yy::parser::symbol_type make_BINARY(const std::string &s, const yy::parser::location_type& loc)
213{
214  errno = 0;
215  long n = strtol (s.c_str()+2, NULL, 2);
216  if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
217    throw yy::parser::syntax_error (loc, "binary is out of range: " + s);
218  return yy::parser::make_INT((int) n, loc);
219}
220
221void pio_assembler::scan_begin ()
222{
223  yy_flex_debug = false;
224  if (source.empty () || source == "-")
225    yyin = stdin;
226  else if (!(yyin = fopen (source.c_str (), "r")))
227    {
228      std::cerr << "cannot open " << source << ": " << strerror(errno) << '\n';
229      exit (EXIT_FAILURE);
230    }
231}
232
233void pio_assembler::scan_end ()
234{
235  fclose (yyin);
236}
237