-%{ // -*-Fundamental-*-
+%{ // -*- mode: c++; c-file-style: "linux" -*-
/*
- lexer.ll -- implement the Flex lexer
+ This file is part of LilyPond, the GNU music typesetter.
- source file of the LilyPond music typesetter
+ Copyright (C) 1996--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ Jan Nieuwenhuizen <janneke@gnu.org>
- (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
- Jan Nieuwenhuizen <janneke@gnu.org>
+ LilyPond is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ LilyPond is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
*/
+/* Mode and indentation are at best a rough approximation based on TAB
+ * formatting (reasonable for compatibility with unspecific editor
+ * modes as Flex modes are hard to find) and need manual correction
+ * frequently. Without a reasonably dependable way of formatting a
+ * Flex file sensibly, there is little point in trying to fix the
+ * inconsistent state of indentation.
+ */
/*
backup rules
using namespace std;
#include "context-def.hh"
-#include "identifier-smob.hh"
+#include "duration.hh"
#include "international.hh"
#include "interval.hh"
#include "lily-guile.hh"
#include "lily-lexer.hh"
+#include "lily-parser.hh"
#include "lilypond-version.hh"
#include "main.hh"
#include "music.hh"
#include "music-function.hh"
#include "parse-scm.hh"
#include "parser.hh"
+#include "pitch.hh"
#include "source-file.hh"
#include "std-string.hh"
#include "string-convert.hh"
yy_push_state (lyric_quote);\
yylval.string = new string
-#define yylval \
- (*(YYSTYPE*)lexval)
+#define yylval (*lexval_)
-#define yylloc \
- (*(YYLTYPE*)lexloc)
+#define yylloc (*lexloc_)
#define YY_USER_ACTION add_lexed_char (YYLeng ());
%x sourcefilename
%x version
+/* The strategy concerning multibyte characters is to accept them but
+ * call YYText_utf8 for patterns that might contain them, in order to
+ * get a single code path responsible for flagging non-UTF-8 input:
+ * Patterns for accepting only valid UTF-8 without backing up are
+ * really hard to do and complex, and if nice error messages are
+ * wanted, one would need patterns catching the invalid input as well.
+ *
+ * Since editors and operating environments don't necessarily behave
+ * reasonably in the presence of mixed encodings, we flag encoding
+ * errors also in identifiers, comments, and strings where it would be
+ * conceivable to just transparently work with the byte string. But
+ * the whole point of caring about UTF-8 in here at all is too avoid
+ * stranger errors later when input passes into backends or log files
+ * or console output or error messages.
+ */
+
A [a-zA-Z\200-\377]
AA {A}|_
N [0-9]
ANY_CHAR (.|\n)
PUNCT [?!:'`]
ACCENT \\[`'"^]
+SPECIAL_CHAR [&@]
NATIONAL [\001-\006\021-\027\031\036]
-TEX {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
-WORD {A}{AN}*
+TEX {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}|{SPECIAL_CHAR}
DASHED_WORD {A}({AN}|-)*
DASHED_KEY_WORD \\{DASHED_WORD}
ALPHAWORD {A}+
-DIGIT {N}
UNSIGNED {N}+
E_UNSIGNED \\{N}+
FRACTION {N}+\/{N}+
INT -?{UNSIGNED}
REAL ({INT}\.{N}*)|(-?\.{N}+)
-KEYWORD \\{WORD}
WHITE [ \n\t\f\r]
HORIZONTALWHITE [ \t]
BLACK [^ \n\t\f\r]
<*>\r {
- // windows-suck-suck-suck
+ // swallow and ignore carriage returns
}
<extratoken>{ANY_CHAR} {
yyless (0);
/* produce requested token */
- int type = extra_token_types_.back ();
- extra_token_types_.pop_back ();
- if (extra_token_types_.empty ())
+ int type = scm_to_int (scm_caar (extra_tokens_));
+ yylval.scm = scm_cdar (extra_tokens_);
+ extra_tokens_ = scm_cdr (extra_tokens_);
+ if (scm_is_null (extra_tokens_))
+ yy_pop_state ();
+
+ return type;
+}
+
+<extratoken><<EOF>> {
+ /* Generate a token without swallowing anything */
+
+ /* produce requested token */
+ int type = scm_to_int (scm_caar (extra_tokens_));
+ yylval.scm = scm_cdar (extra_tokens_);
+ extra_tokens_ = scm_cdr (extra_tokens_);
+ if (scm_is_null (extra_tokens_))
yy_pop_state ();
return type;
/* Use the trailing context feature. Otherwise, the BOM will not be
found if the file starts with an identifier definition. */
<INITIAL,chords,lyrics,figures,notes>{BOM_UTF8}/.* {
- if (this->lexloc->line_number () != 1 || this->lexloc->column_number () != 0)
+ if (this->lexloc_->line_number () != 1 || this->lexloc_->column_number () != 0)
{
- LexerError (_ ("stray UTF-8 BOM encountered").c_str ());
- exit (1);
+ LexerWarning (_ ("stray UTF-8 BOM encountered").c_str ());
+ // exit (1);
}
- if (be_verbose_global)
- message (_ ("Skipping UTF-8 BOM"));
+ debug_output (_ ("Skipping UTF-8 BOM"));
}
<INITIAL,chords,figures,incl,lyrics,markup,notes>{
"%{" {
yy_push_state (longcomment);
}
- %[^{\n\r].*[\n\r] {
+ %[^{\n\r][^\n\r]*[\n\r] {
+ (void) YYText_utf8 ();
}
%[^{\n\r] { // backup rule
+ (void) YYText_utf8 ();
}
%[\n\r] {
}
- %[^{\n\r].* {
+ %[^{\n\r][^\n\r]* {
+ (void) YYText_utf8 ();
}
{WHITE}+ {
yy_push_state (sourcefileline);
}
<version>\"[^"]*\" { /* got the version number */
- string s (YYText () + 1);
+ string s (YYText_utf8 () + 1);
s = s.substr (0, s.rfind ('\"'));
yy_pop_state ();
}
<sourcefilename>\"[^"]*\" {
- string s (YYText () + 1);
+ string s (YYText_utf8 () + 1);
s = s.substr (0, s.rfind ('\"'));
yy_pop_state ();
this->here_input ().get_source_file ()->set_line (here_input ().start (), i);
}
-<version>. {
+<version>{ANY_CHAR} {
LexerError (_ ("quoted string expected after \\version").c_str ());
yy_pop_state ();
}
-<sourcefilename>. {
+<sourcefilename>{ANY_CHAR} {
LexerError (_ ("quoted string expected after \\sourcefilename").c_str ());
yy_pop_state ();
}
-<sourcefileline>. {
+<sourcefileline>{ANY_CHAR} {
LexerError (_ ("integer expected after \\sourcefileline").c_str ());
yy_pop_state ();
}
<longcomment>{
[^\%]* {
+ (void) YYText_utf8 ();
}
\%*[^}%]* {
-
+ (void) YYText_utf8 ();
}
"%"+"}" {
yy_pop_state ();
}
- <<EOF>> {
- LexerError (_ ("EOF found inside a comment").c_str ());
- is_main_input_ = false; // should be safe , can't have \include in --safe.
- if (! close_input ())
- yyterminate (); // can't move this, since it actually rets a YY_NULL
- }
}
<INITIAL,chords,lyrics,figures,notes>\\include {
yy_push_state (incl);
}
-<incl>\"[^"]*\" { /* got the include file name */
- string s (YYText ()+1);
+<incl>\"[^""]*\" { /* got the include file name */
+ string s (YYText_utf8 ()+1);
s = s.substr (0, s.rfind ('"'));
new_input (s, sources_);
yy_pop_state ();
}
-<incl>\\{BLACK}*{WHITE} { /* got the include identifier */
- string s = YYText () + 1;
+<incl>\\{BLACK}*{WHITE}? { /* got the include identifier */
+ string s = YYText_utf8 () + 1;
strip_trailing_white (s);
if (s.length () && (s[s.length () - 1] == ';'))
s = s.substr (0, s.length () - 1);
if (scm_is_string (sid)) {
new_input (ly_scm2string (sid), sources_);
yy_pop_state ();
- } else {
+ } else {
string msg (_f ("wrong or undefined identifier: `%s'", s ));
LexerError (msg.c_str ());
scm_display (sid, err);
}
}
-<incl>\"[^"]* { // backup rule
+<incl>(\$|#) { // scm for the filename
+ int n = 0;
+ Input hi = here_input();
+ hi.step_forward ();
+ SCM sval = ly_parse_scm (hi.start (), &n, hi,
+ be_safe_global && is_main_input_, parser_);
+ sval = eval_scm (sval);
+
+ for (int i = 0; i < n; i++)
+ {
+ yyinput ();
+ }
+ char_count_stack_.back () += n;
+
+ if (scm_is_string (sval)) {
+ new_input (ly_scm2string (sval), sources_);
+ yy_pop_state ();
+ } else {
+ LexerError (_ ("string expected after \\include").c_str ());
+ if (sval != SCM_UNDEFINED) {
+ SCM err = scm_current_error_port ();
+ scm_puts ("This value was found instead: ", err);
+ scm_display (sval, err);
+ }
+ }
+}
+
+<incl,version,sourcefilename>\"[^""]* { // backup rule
error (_ ("end quote missing"));
exit (1);
}
yylval.scm = scm_from_locale_string (s);
return RESTNAME;
}
+<chords,notes,figures>q {
+ return CHORD_REPETITION;
+}
+
<chords,notes,figures>R {
return MULTI_MEASURE_REST;
}
be_safe_global && is_main_input_, parser_);
if (sval == SCM_UNDEFINED)
- {
- sval = SCM_UNSPECIFIED;
error_level_ = 1;
- }
for (int i = 0; i < n; i++)
{
}
char_count_stack_.back () += n;
- if (unpack_identifier (sval) != SCM_UNDEFINED)
+ yylval.scm = sval;
+ return SCM_TOKEN;
+}
+
+<INITIAL,chords,figures,lyrics,markup,notes>\$ { //immediate scm
+ int n = 0;
+ Input hi = here_input();
+ hi.step_forward ();
+ SCM sval = ly_parse_scm (hi.start (), &n, hi,
+ be_safe_global && is_main_input_, parser_);
+
+ for (int i = 0; i < n; i++)
{
- yylval.scm = unpack_identifier(sval);
- return identifier_type (yylval.scm);
+ yyinput ();
}
+ char_count_stack_.back () += n;
+
+ sval = eval_scm (sval);
- yylval.scm = sval;
- return SCM_TOKEN;
+ int token = scan_scm_id (sval);
+ if (!scm_is_eq (yylval.scm, SCM_UNSPECIFIED))
+ return token;
}
+
<INITIAL,notes,lyrics>{
\<\< {
return DOUBLE_ANGLE_OPEN;
<notes,figures>{
{ALPHAWORD} {
- return scan_bare_word (YYText ());
+ return scan_bare_word (YYText_utf8 ());
}
{NOTECOMMAND} {
- return scan_escaped_word (YYText () + 1);
+ return scan_escaped_word (YYText_utf8 () + 1);
}
{FRACTION} {
yylval.scm = scan_fraction (YYText ());
return FRACTION;
}
-
- {DIGIT} {
- yylval.i = String_convert::dec2int (string (YYText ()));
- return DIGIT;
- }
+ {UNSIGNED}/\/ | // backup rule
{UNSIGNED} {
- yylval.i = String_convert::dec2int (string (YYText ()));
+ yylval.scm = scm_c_read_string (YYText ());
return UNSIGNED;
}
{E_UNSIGNED} {
\\{ESCAPED} {
*yylval.string += to_string (escaped_char (YYText ()[1]));
}
- [^\\"]+ {
- *yylval.string += YYText ();
+ [^\\""]+ {
+ *yylval.string += YYText_utf8 ();
}
\" {
delete sp;
return is_lyric_state () ? LYRICS_STRING : STRING;
}
- . {
+ \\ {
*yylval.string += YYText ();
}
}
yylval.scm = scan_fraction (YYText ());
return FRACTION;
}
+ {UNSIGNED}/\/[^0-9] { // backup rule
+ yylval.scm = scm_c_read_string (YYText ());
+ return UNSIGNED;
+ }
+ {UNSIGNED}/\/ | // backup rule
{UNSIGNED} {
- yylval.i = String_convert::dec2int (string (YYText ()));
+ yylval.scm = scm_c_read_string (YYText ());
return UNSIGNED;
}
{NOTECOMMAND} {
- return scan_escaped_word (YYText () + 1);
+ return scan_escaped_word (YYText_utf8 () + 1);
}
{LYRICS} {
/* ugr. This sux. */
- string s (YYText ());
+ string s (YYText_utf8 ());
if (s == "__")
return yylval.i = EXTENDER;
if (s == "--")
return LYRICS_STRING;
}
. {
- return YYText ()[0];
+ return YYText ()[0]; // LYRICS already catches all multibytes.
}
}
<chords>{
{ALPHAWORD} {
- return scan_bare_word (YYText ());
+ return scan_bare_word (YYText_utf8 ());
}
{NOTECOMMAND} {
- return scan_escaped_word (YYText () + 1);
+ return scan_escaped_word (YYText_utf8 () + 1);
}
{FRACTION} {
yylval.scm = scan_fraction (YYText ());
return FRACTION;
}
+ {UNSIGNED}/\/[^0-9] { // backup rule
+ yylval.scm = scm_c_read_string (YYText ());
+ return UNSIGNED;
+ }
+ {UNSIGNED}/\/ | // backup rule
{UNSIGNED} {
- yylval.i = String_convert::dec2int (string (YYText ()));
+ yylval.scm = scm_c_read_string (YYText ());
return UNSIGNED;
}
- {
return CHORD_CARET;
}
. {
- return YYText ()[0];
+ return YYText ()[0]; // ALPHAWORD catches all multibyte.
}
}
return SCORE;
}
{MARKUPCOMMAND} {
- string str (YYText () + 1);
+ string str (YYText_utf8 () + 1);
+
+ int token_type = MARKUP_FUNCTION;
SCM s = lookup_markup_command (str);
- SCM s2 = lookup_markup_list_command (str);
- if (scm_is_pair (s) && scm_is_symbol (scm_cdr (s)) ) {
- yylval.scm = scm_car(s);
- SCM tag = scm_cdr(s);
- if (tag == ly_symbol2scm("markup0"))
- return MARKUP_HEAD_MARKUP0;
- if (tag == ly_symbol2scm("empty"))
- return MARKUP_HEAD_EMPTY;
- else if (tag == ly_symbol2scm ("markup0-markup1"))
- return MARKUP_HEAD_MARKUP0_MARKUP1;
- else if (tag == ly_symbol2scm ("markup-list0"))
- return MARKUP_HEAD_LIST0;
- else if (tag == ly_symbol2scm ("scheme0"))
- return MARKUP_HEAD_SCM0;
- else if (tag == ly_symbol2scm ("scheme0-scheme1"))
- return MARKUP_HEAD_SCM0_SCM1;
- else if (tag == ly_symbol2scm ("scheme0-markup1"))
- return MARKUP_HEAD_SCM0_MARKUP1;
- else if (tag == ly_symbol2scm ("scheme0-scheme1-markup2"))
- return MARKUP_HEAD_SCM0_SCM1_MARKUP2;
- else if (tag == ly_symbol2scm ("scheme0-scheme1-markup2-markup3"))
- return MARKUP_HEAD_SCM0_SCM1_MARKUP2_MARKUP3;
- else if (tag == ly_symbol2scm ("scheme0-markup1-markup2"))
- return MARKUP_HEAD_SCM0_MARKUP1_MARKUP2;
- else if (tag == ly_symbol2scm ("scheme0-scheme1-scheme2"))
- return MARKUP_HEAD_SCM0_SCM1_SCM2;
- else {
- programming_error ("no parser tag defined for this markup signature");
- ly_display_scm (s);
- assert(false);
- }
- } else if (scm_is_pair (s2) && scm_is_symbol (scm_cdr (s2))) {
- yylval.scm = scm_car(s2);
- SCM tag = scm_cdr(s2);
- if (tag == ly_symbol2scm("empty"))
- return MARKUP_LIST_HEAD_EMPTY;
- else if (tag == ly_symbol2scm ("scheme0"))
- return MARKUP_LIST_HEAD_SCM0;
- else if (tag == ly_symbol2scm ("markup-list0"))
- return MARKUP_LIST_HEAD_LIST0;
- else if (tag == ly_symbol2scm ("scheme0-markup-list1"))
- return MARKUP_LIST_HEAD_SCM0_LIST1;
- else if (tag == ly_symbol2scm ("scheme0-scheme1-markup-list2"))
- return MARKUP_LIST_HEAD_SCM0_SCM1_LIST2;
- else {
- programming_error ("no parser tag defined for this markup list signature");
- ly_display_scm (s);
- assert(false);
- }
- } else
- return scan_escaped_word (str);
+
+ // lookup-markup-command returns a pair with the car
+ // being the function to call, and the cdr being the
+ // call signature specified to define-markup-command,
+ // a list of predicates.
+
+ if (!scm_is_pair (s)) {
+ // If lookup-markup-command was not successful, we
+ // try lookup-markup-list-command instead.
+ // If this fails as well, we just scan and return
+ // the escaped word.
+ s = lookup_markup_list_command (str);
+ if (scm_is_pair (s))
+ token_type = MARKUP_LIST_FUNCTION;
+ else
+ return scan_escaped_word (str);
+ }
+
+ // If the list of predicates is, say,
+ // (number? number? markup?), then tokens
+ // EXPECT_MARKUP EXPECT_SCM EXPECT_SCM EXPECT_NO_MORE_ARGS
+ // will be generated. Note that we have to push them
+ // in reverse order, so the first token pushed in the
+ // loop will be EXPECT_NO_MORE_ARGS.
+
+ yylval.scm = scm_car(s);
+
+ // yylval now contains the function to call as token
+ // value (for token type MARKUP_FUNCTION or
+ // MARKUP_LIST_FUNCTION).
+
+ push_extra_token(EXPECT_NO_MORE_ARGS);
+ s = scm_cdr(s);
+ for (; scm_is_pair(s); s = scm_cdr(s)) {
+ SCM predicate = scm_car(s);
+
+ if (predicate == ly_lily_module_constant ("markup-list?"))
+ push_extra_token(EXPECT_MARKUP_LIST);
+ else if (predicate == ly_lily_module_constant ("markup?"))
+ push_extra_token(EXPECT_MARKUP);
+ else
+ push_extra_token(EXPECT_SCM, predicate);
+ }
+ return token_type;
}
[{}] {
return YYText ()[0];
}
- [^#{}"\\ \t\n\r\f]+ {
- string s (YYText ());
+ [^$#{}\"\\ \t\n\r\f]+ {
+ string s (YYText_utf8 ());
char c = s[s.length () - 1];
/* brace open is for not confusing dumb tools. */
return STRING;
}
. {
- return YYText()[0];
+ return YYText()[0]; // Above is catchall for multibyte
}
}
-<*><<EOF>> {
- if (is_main_input_)
+<longcomment><<EOF>> {
+ LexerError (_ ("EOF found inside a comment").c_str ());
+ is_main_input_ = false; // should be safe , can't have \include in --safe.
+ if (!close_input ())
+ yyterminate (); // can't move this, since it actually rets a YY_NULL
+ }
+
+<<EOF>> { if (is_main_input_)
{
/* 2 = init.ly + current file.
> because we're before closing, but is_main_input_ should
<INITIAL>{
{DASHED_WORD} {
- return scan_bare_word (YYText ());
+ return scan_bare_word (YYText_utf8 ());
}
{DASHED_KEY_WORD} {
- return scan_escaped_word (YYText () + 1);
+ return scan_escaped_word (YYText_utf8 () + 1);
}
}
-{WORD} {
- return scan_bare_word (YYText ());
-}
-{KEYWORD} {
- return scan_escaped_word (YYText () + 1);
-}
+-{UNSIGNED} | // backup rule
{REAL} {
- Real r;
- int cnv = sscanf (YYText (), "%lf", &r);
- assert (cnv == 1);
- (void) cnv;
-
- yylval.scm = scm_from_double (r);
+ yylval.scm = scm_c_read_string (YYText ());
+ return REAL;
+}
+-\. { // backup rule
+ yylval.scm = scm_from_double (0.0);
return REAL;
}
{UNSIGNED} {
- yylval.i = String_convert::dec2int (string (YYText ()));
+ yylval.scm = scm_c_read_string (YYText ());
return UNSIGNED;
}
}
}
-<*>. {
- string msg = _f ("invalid character: `%c'", YYText ()[0]);
+<*>.[\200-\277]* {
+ string msg = _f ("invalid character: `%s'", YYText_utf8 ());
LexerError (msg.c_str ());
- return YYText ()[0];
+ return '%'; // Better not return half a utf8 character.
}
%%
/* Make the lexer generate a token of the given type as the next token.
TODO: make it possible to define a value for the token as well */
void
-Lily_lexer::push_extra_token (int token_type)
+Lily_lexer::push_extra_token (int token_type, SCM scm)
{
- if (extra_token_types_.empty ())
+ if (scm_is_null (extra_tokens_))
{
if (YY_START != extratoken)
hidden_state_ = YY_START;
yy_push_state (extratoken);
}
- extra_token_types_.push_back (token_type);
+ extra_tokens_ = scm_acons (scm_from_int (token_type), scm, extra_tokens_);
}
void
void
Lily_lexer::pop_state ()
{
+ bool extra = (YYSTATE == extratoken);
+
+ if (extra)
+ yy_pop_state ();
+
if (YYSTATE == notes || YYSTATE == chords)
pitchname_tab_stack_ = scm_cdr (pitchname_tab_stack_);
yy_pop_state ();
+
+ if (extra) {
+ hidden_state_ = YYSTATE;
+ yy_push_state (extratoken);
+ }
}
int
return i;
SCM sid = lookup_identifier (str);
+ if (sid != SCM_UNDEFINED)
+ return scan_scm_id (sid);
+
+ string msg (_f ("unknown escaped string: `\\%s'", str));
+ LexerError (msg.c_str ());
+
+ yylval.scm = ly_string2scm (str);
+
+ return STRING;
+}
+
+int
+Lily_lexer::scan_scm_id (SCM sid)
+{
if (is_music_function (sid))
{
+ int funtype = SCM_FUNCTION;
+
yylval.scm = get_music_function_transform (sid);
SCM s = scm_object_property (yylval.scm, ly_symbol2scm ("music-function-signature"));
- push_extra_token (EXPECT_NO_MORE_ARGS);
- for (; scm_is_pair (s); s = scm_cdr (s))
+ SCM cs = scm_car (s);
+
+ if (scm_is_pair (cs))
{
- if (scm_car (s) == ly_music_p_proc)
- push_extra_token (EXPECT_MUSIC);
- else if (scm_car (s) == ly_lily_module_constant ("markup?"))
- push_extra_token (EXPECT_MARKUP);
- else if (ly_is_procedure (scm_car (s)))
- push_extra_token (EXPECT_SCM);
- else programming_error ("Function parameter without type-checking predicate");
+ cs = SCM_CAR (cs);
}
- return MUSIC_FUNCTION;
- }
- if (sid != SCM_UNDEFINED)
- {
- yylval.scm = sid;
- return identifier_type (sid);
- }
+ if (scm_is_eq (cs, ly_lily_module_constant ("ly:music?")))
+ funtype = MUSIC_FUNCTION;
+ else if (scm_is_eq (cs, ly_lily_module_constant ("ly:event?")))
+ funtype = EVENT_FUNCTION;
+ else if (ly_is_procedure (cs))
+ funtype = SCM_FUNCTION;
+ else programming_error ("Bad syntax function predicate");
- string msg (_f ("unknown escaped string: `\\%s'", str));
- LexerError (msg.c_str ());
-
- yylval.scm = ly_string2scm (str);
+ push_extra_token (EXPECT_NO_MORE_ARGS);
+ for (s = scm_cdr (s); scm_is_pair (s); s = scm_cdr (s))
+ {
+ SCM optional = SCM_UNDEFINED;
+ cs = scm_car (s);
- return STRING;
+ if (scm_is_pair (cs))
+ {
+ optional = SCM_CDR (cs);
+ cs = SCM_CAR (cs);
+ }
+
+ if (cs == Pitch_type_p_proc)
+ push_extra_token (EXPECT_PITCH);
+ else if (cs == Duration_type_p_proc)
+ push_extra_token (EXPECT_DURATION);
+ else if (ly_is_procedure (cs))
+ push_extra_token (EXPECT_SCM, cs);
+ else
+ {
+ programming_error ("Function parameter without type-checking predicate");
+ continue;
+ }
+ if (!scm_is_eq (optional, SCM_UNDEFINED))
+ push_extra_token (EXPECT_OPTIONAL, optional);
+ }
+ return funtype;
+ }
+ yylval.scm = sid;
+ return identifier_type (sid);
}
int
return CHORD_MODIFIER;
}
}
-
yylval.scm = ly_string2scm (str);
return STRING;
}
return get_state () == figures;
}
+SCM
+Lily_lexer::eval_scm (SCM readerdata)
+{
+ SCM sval = SCM_UNDEFINED;
+
+ if (!SCM_UNBNDP (readerdata))
+ {
+ sval = ly_eval_scm (scm_car (readerdata),
+ *unsmob_input (scm_cdr (readerdata)),
+ be_safe_global && is_main_input_,
+ parser_);
+ }
+
+ if (SCM_UNBNDP (sval))
+ {
+ error_level_ = 1;
+ return SCM_UNSPECIFIED;
+ }
+ return sval;
+}
+
+/* Check for valid UTF-8 that has no overlong or surrogate codes and
+ is in the range 0-0x10ffff */
+
+const char *
+Lily_lexer::YYText_utf8 ()
+{
+ const char * const p = YYText ();
+ for (int i=0; p[i];) {
+ if ((p[i] & 0xff) < 0x80) {
+ ++i;
+ continue;
+ }
+ int oldi = i; // start of character
+ int more = 0; // # of followup bytes, 0 if bad
+ switch (p[i++] & 0xff) {
+ // 0xc0 and 0xc1 are overlong prefixes for
+ // 0x00-0x3f and 0x40-0x7f respectively, bad.
+ case 0xc2: // 0x80-0xbf
+ case 0xc3: // 0xc0-0xff
+ case 0xc4: // 0x100-0x13f
+ case 0xc5: // 0x140-0x17f
+ case 0xc6: // 0x180-0x1bf
+ case 0xc7: // 0x1c0-0x1ff
+ case 0xc8: // 0x200-0x23f
+ case 0xc9: // 0x240-0x27f
+ case 0xca: // 0x280-0x2bf
+ case 0xcb: // 0x2c0-0x2ff
+ case 0xcc: // 0x300-0x33f
+ case 0xcd: // 0x340-0x37f
+ case 0xce: // 0x380-0x3bf
+ case 0xcf: // 0x3c0-0x3ff
+ case 0xd0: // 0x400-0x43f
+ case 0xd1: // 0x440-0x47f
+ case 0xd2: // 0x480-0x4bf
+ case 0xd3: // 0x4c0-0x4ff
+ case 0xd4: // 0x500-0x53f
+ case 0xd5: // 0x540-0x57f
+ case 0xd6: // 0x580-0x5bf
+ case 0xd7: // 0x5c0-0x5ff
+ case 0xd8: // 0x600-0x63f
+ case 0xd9: // 0x640-0x67f
+ case 0xda: // 0x680-0x6bf
+ case 0xdb: // 0x6c0-0x6ff
+ case 0xdc: // 0x700-0x73f
+ case 0xdd: // 0x740-0x77f
+ case 0xde: // 0x780-0x7bf
+ case 0xdf: // 0x7c0-0x7ff
+ more = 1; // 2-byte sequences, 0x80-0x7ff
+ break;
+ case 0xe0:
+ // don't allow overlong sequences for 0-0x7ff
+ if ((p[i] & 0xff) < 0xa0)
+ break;
+ case 0xe1: // 0x1000-0x1fff
+ case 0xe2: // 0x2000-0x2fff
+ case 0xe3: // 0x3000-0x3fff
+ case 0xe4: // 0x4000-0x4fff
+ case 0xe5: // 0x5000-0x5fff
+ case 0xe6: // 0x6000-0x6fff
+ case 0xe7: // 0x7000-0x7fff
+ case 0xe8: // 0x8000-0x8fff
+ case 0xe9: // 0x9000-0x9fff
+ case 0xea: // 0xa000-0xafff
+ case 0xeb: // 0xb000-0xbfff
+ case 0xec: // 0xc000-0xcfff
+ more = 2; // 3-byte sequences, 0x7ff-0xcfff
+ break;
+ case 0xed: // 0xd000-0xdfff
+ // Don't allow surrogate codes 0xd800-0xdfff
+ if ((p[i] & 0xff) >= 0xa0)
+ break;
+ case 0xee: // 0xe000-0xefff
+ case 0xef: // 0xf000-0xffff
+ more = 2; // 3-byte sequences,
+ // 0xd000-0xd7ff, 0xe000-0xffff
+ break;
+ case 0xf0:
+ // don't allow overlong sequences for 0-0xffff
+ if ((p[i] & 0xff) < 0x90)
+ break;
+ case 0xf1: // 0x40000-0x7ffff
+ case 0xf2: // 0x80000-0xbffff
+ case 0xf3: // 0xc0000-0xfffff
+ more = 3; // 4-byte sequences, 0x10000-0xfffff
+ break;
+ case 0xf4:
+ // don't allow more than 0x10ffff
+ if ((p[i] & 0xff) >= 0x90)
+ break;
+ more = 3; // 4-byte sequence, 0x100000-0x10ffff
+ break;
+ }
+ if (more) {
+ // check that all continuation bytes are valid
+ do {
+ if ((p[i++] & 0xc0) != 0x80)
+ break;
+ } while (--more);
+ if (!more)
+ continue;
+ }
+ Input h = here_input ();
+ h.set (h.get_source_file (), h.start () + oldi, h.start () + i);
+ h.warning (_ ("non-UTF-8 input").c_str ());
+ }
+ return p;
+}
+
+
/*
urg, belong to string (_convert)
and should be generalised
/*
- substitute _ and \,
+ substitute _
*/
string
lyric_fudge (string s)
{
- char *chars = string_copy (s);
+ size_t i=0;
- for (char *p = chars; *p ; p++)
- {
- if (*p == '_' && (p == chars || *(p-1) != '\\'))
- *p = ' ';
- }
-
- s = string (chars);
- delete[] chars;
-
- ssize i = 0;
- if ((i = s.find ("\\,")) != NPOS) // change "\," to TeX's "\c "
- {
- * (((char*)s.c_str ()) + i + 1) = 'c';
- s = s.substr (0, i + 2) + " " + s.substr (i - 2);
- }
-
- return s;
+ while ((i = s.find ('_', i)) != string::npos)
+ {
+ s[i++] = ' ';
+ }
+ return s;
}
/*