1/** @file ginsh_lexer.lpp
2 *
3 *  Lexical analyzer definition for ginsh.
4 *  This file must be processed with flex. */
5
6/*
7 *  GiNaC Copyright (C) 1999-2022 Johannes Gutenberg University Mainz, Germany
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 */
23
24
25/*
26 *  Definitions
27 */
28
29%option nounput
30
31%pointer
32
33%{
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include "ginsh.h"
39#include "ginsh_parser.hpp"
40
41using namespace std;
42using namespace GiNaC;
43
44#define YY_INPUT(buf, result, max_size) (result = ginsh_input(buf, max_size))
45
46// Table of all used symbols
47sym_tab syms;
48
49// Type of symbols to generate (real or complex)
50unsigned symboltype = domain::complex;
51
52// lex input function
53static int ginsh_input(char *buf, int max_size);
54%}
55
56	/* Abbreviations */
57D	[0-9]
58E	[elEL][-+]?{D}+
59A	[a-zA-Z_]
60AN	[0-9a-zA-Z_]
61
62
63/*
64 *  Lexical rules
65 */
66
67%%
68[ \t\n]+		/* skip whitespace */
69\\$			/* skip line continuations */
70"//".*			/* skip comments starting with "//" */
71^"#".*			/* skip lines starting with "#" */
72^"!".*			system(yytext + 1);	/* execute shell command */
73
74			/* special values */
75Pi			yylval = Pi; return T_LITERAL;
76Euler			yylval = Euler; return T_LITERAL;
77Catalan			yylval = Catalan; return T_LITERAL;
78FAIL			yylval = *new fail(); return T_LITERAL;
79I			yylval = I; return T_NUMBER;
80Digits			yylval = (long)Digits; return T_DIGITS;
81
82			/* keywords */
83quit|exit		return T_QUIT;
84warranty		return T_WARRANTY;
85print			return T_PRINT;
86iprint			return T_IPRINT;
87print_latex		return T_PRINTLATEX;
88print_csrc		return T_PRINTCSRC;
89time			return T_TIME;
90xyzzy			return T_XYZZY;
91inventory		return T_INVENTORY;
92look			return T_LOOK;
93score			return T_SCORE;
94complex_symbols return T_COMPLEX_SYMBOLS;
95real_symbols    return T_REAL_SYMBOLS;
96
97			/* comparison */
98"=="			return T_EQUAL;
99"!="			return T_NOTEQ;
100"<="			return T_LESSEQ;
101">="			return T_GREATEREQ;
102
103			/* last 1..3 expressions */
104\%			return T_QUOTE;
105\%\%			return T_QUOTE2;
106\%\%\%			return T_QUOTE3;
107
108			/* numbers */
109{D}+			|
110"#"{D}+"R"{AN}+		|
111"#b"([01])+		|
112"#o"[0-7]+		|
113"#x"[0-9a-fA-F]+	|
114{D}+"."{D}*({E})?	|
115{D}*"."{D}+({E})?	|
116{D}+{E}			yylval = numeric(yytext); return T_NUMBER;
117
118			/* symbols */
119{A}{AN}*		{
120				sym_tab::const_iterator i = syms.find(yytext);
121				if (i == syms.end()) {
122					if (symboltype == domain::complex) {
123						symbol tmp(yytext);
124						syms[yytext] = tmp;
125						yylval = tmp;
126					} else {
127						realsymbol tmp(yytext);
128						syms[yytext] = tmp;
129						yylval = tmp;
130					}
131				} else
132					yylval = i->second;
133				return T_SYMBOL;
134			}
135
136			/* wildcards */
137\${D}+			yylval = wild(atoi(yytext + 1)); return T_LITERAL;
138
139			/* everything else */
140.			return *yytext;
141
142%%
143
144
145/*
146 *  Routines
147 */
148
149static int line_length = 0;
150static char *line_read = nullptr;
151static char *line_ptr;
152
153// Input function that uses libreadline for interactive input
154static int ginsh_input(char *buf, int max_size)
155{
156	int result;
157#if defined(YY_CURRENT_BUFFER)
158	if (YY_CURRENT_BUFFER->yy_is_interactive) {
159#else
160	if (yy_current_buffer->yy_is_interactive) {
161#endif
162#ifdef HAVE_LIBREADLINE
163		// Do we need to read a new line?
164		int actual;
165		if (line_length == 0) {
166
167			// Free old line
168			if (line_read)
169				free(line_read);
170
171			// Read new line, prompt "> "
172			line_read = line_ptr = readline("> ");
173
174			// EOF?
175			if (!line_read) {
176				line_length = 0;
177				return YY_NULL;
178			}
179
180			// Add non-empty lines to history
181			line_length = strlen(line_read) + 1;
182			if (line_length > 1)
183				add_history(line_read);
184
185			// Reappend trailing '\n' which is stripped by readline()
186			line_read[line_length - 1] = '\n';
187		}
188
189		// Copy data to lex buffer
190		actual = line_length > max_size ? max_size : line_length;
191		memcpy(buf, line_ptr, actual);
192		line_length -= actual;
193		line_ptr += actual;
194		result = actual;
195#else
196		printf("> "); fflush(stdout);
197		int c = '*', n;
198		for (n = 0; n < max_size && (c = getc(yyin)) != EOF && c != '\n'; ++n)
199			buf[n] = (char)c;
200		if (c == '\n')
201			buf[n++] = (char)c;
202		if (c == EOF && ferror(yyin))
203			YY_FATAL_ERROR("input in flex scanner failed");
204		result = n;
205#endif
206	} else if (((result = fread(buf, 1, max_size, yyin)) == 0) && ferror(yyin))
207		YY_FATAL_ERROR("input in flex scanner failed");
208
209	return result;
210}
211
212// List of input files to be processed
213int num_files = 0;
214char **file_list = nullptr;
215
216// EOF encountered, connect to next file. If this was the last file,
217// connect to stdin. If this was stdin, terminate the scanner.
218int yywrap()
219{
220	if (yyin == stdin)
221		return 1;
222
223	fclose(yyin);
224	if (num_files) {
225		yyin = fopen(*file_list, "r");
226		if (yyin == nullptr) {
227			cerr << "Can't open " << *file_list << endl;
228			return 1;
229		}
230		num_files--;
231		file_list++;
232	} else
233		yyin = stdin;
234	return 0;
235}
236