-%{ // -*-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--2006 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 "lilypond-input-version.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"
void strip_leading_white (string&);
string lyric_fudge (string s);
SCM lookup_markup_command (string s);
+SCM lookup_markup_list_command (string s);
bool is_valid_version (string s);
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 ());
-/*
-
-LYRICS ({AA}|{TEX})[^0-9 \t\n\f]*
-
-*/
SCM scan_fraction (string);
%x sourcefilename
%x version
-A [a-zA-Z]
+/* 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]
-AN {AA}|{N}
ANY_CHAR (.|\n)
-PUNCT [?!:'`]
-ACCENT \\[`'"^]
-NATIONAL [\001-\006\021-\027\031\036\200-\377]
-TEX {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
-WORD {A}{AN}*
-DASHED_WORD {A}({AN}|-)*
-DASHED_KEY_WORD \\{DASHED_WORD}
-ALPHAWORD {A}+
-DIGIT {N}
+PUNCT [][()?!:'`]
+SPECIAL_CHAR [&@]
+NATIONAL [\001-\006\021-\027\031\036]
+TEX {AA}|-|{PUNCT}|{NATIONAL}|{SPECIAL_CHAR}
+WORD {A}([-_]{A}|{A})*
+COMMAND \\{WORD}
+
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]
RESTNAME [rs]
-NOTECOMMAND \\{A}+
-MARKUPCOMMAND \\({A}|[-_])+
LYRICS ({AA}|{TEX})[^0-9 \t\n\r\f]*
ESCAPED [nt\\'"]
EXTENDER __
HYPHEN --
BOM_UTF8 \357\273\277
+
%%
<*>\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;
}
-<INITIAL,chords,lyrics,figures,notes>{BOM_UTF8} {
- if (this->lexloc->line_number () != 1 || this->lexloc->column_number () != 0)
+<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)
{
- 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);
+<sourcefilename>\"[^""]*\" {
+ string s (YYText_utf8 () + 1);
s = s.substr (0, s.rfind ('\"'));
yy_pop_state ();
progress_indication ("\n");
scm_module_define (scm_car (scopes_),
ly_symbol2scm ("input-file-name"),
- scm_makfrom0str (s.c_str ()));
+ ly_string2scm (s));
}
int i;
sscanf (YYText (), "%d", &i);
-// this->set_debug (1);
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;
- 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);
}
+
+ /* Flex picks the longest matching pattern including trailing
+ * contexts. Without the backup pattern, r-. does not trigger the
+ * {RESTNAME} rule but rather the {WORD}/[-_] rule coming later,
+ * needed for avoiding backup states.
+ */
+
+<chords,notes,figures>{RESTNAME}/[-_] | // pseudo backup rule
<chords,notes,figures>{RESTNAME} {
char const *s = YYText ();
- yylval.scm = scm_makfrom0str (s);
+ yylval.scm = scm_from_locale_string (s);
return RESTNAME;
}
+<chords,notes,figures>q/[-_] | // pseudo backup rule
+<chords,notes,figures>q {
+ return CHORD_REPETITION;
+}
+
+<chords,notes,figures>R/[-_] | // pseudo backup rule
<chords,notes,figures>R {
return MULTI_MEASURE_REST;
}
Input hi = here_input();
hi.step_forward ();
SCM sval = ly_parse_scm (hi.start (), &n, hi,
- be_safe_global && is_main_input_);
+ 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 = unpack_identifier(sval);
- return identifier_type (yylval.scm);
- }
-
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++)
+ {
+ yyinput ();
+ }
+ char_count_stack_.back () += n;
+
+ sval = eval_scm (sval, '$');
+
+ 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 ());
+ {WORD}/[-_] | // backup rule
+ {WORD} {
+ return scan_bare_word (YYText_utf8 ());
}
- {NOTECOMMAND} {
- return scan_escaped_word (YYText () + 1);
+ {COMMAND}/[-_] | // backup rule
+ {COMMAND} {
+ 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 ();
}
\" {
/* yylval is union. Must remember STRING before setting SCM*/
string *sp = yylval.string;
- yylval.scm = scm_makfrom0str (sp->c_str ());
+ yylval.scm = ly_string2scm (*sp);
delete sp;
return is_lyric_state () ? LYRICS_STRING : STRING;
}
- . {
+ \\ {
*yylval.string += YYText ();
}
}
yylval.scm = scan_fraction (YYText ());
return FRACTION;
}
+ {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);
+ {COMMAND}/[-_] | // backup rule
+ {COMMAND} {
+ 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 == "--")
if (c == '{' || c == '}') // brace open is for not confusing dumb tools.
here_input ().warning (
_ ("Brace found at end of lyric. Did you forget a space?"));
- yylval.scm = scm_makfrom0str (s.c_str ());
+ yylval.scm = ly_string2scm (s);
return LYRICS_STRING;
}
. {
- return YYText ()[0];
+ return YYText ()[0]; // LYRICS already catches all multibytes.
}
}
<chords>{
- {ALPHAWORD} {
- return scan_bare_word (YYText ());
+ {WORD}/[-_] | // backup rule
+ {WORD} {
+ return scan_bare_word (YYText_utf8 ());
}
- {NOTECOMMAND} {
- return scan_escaped_word (YYText () + 1);
+ {COMMAND}/[-_] | // backup rule
+ {COMMAND} {
+ return scan_escaped_word (YYText_utf8 () + 1);
}
{FRACTION} {
yylval.scm = scan_fraction (YYText ());
return FRACTION;
}
+ {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]; // WORD catches all multibyte.
}
}
\\score {
return SCORE;
}
- {MARKUPCOMMAND} {
- string str (YYText () + 1);
+ {COMMAND}/[-_] | // backup rule
+ {COMMAND} {
+ string str (YYText_utf8 () + 1);
+
+ int token_type = MARKUP_FUNCTION;
SCM s = lookup_markup_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-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
- 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. */
if (c == '{' || c == '}')
here_input ().warning (
_ ("Brace found at end of markup. Did you forget a space?"));
- yylval.scm = scm_makfrom0str (s.c_str ());
+ yylval.scm = ly_string2scm (s);
return STRING;
}
. {
- return YYText()[0];
+ return YYText()[0]; // Above is catchall for multibyte
}
}
-<*><<EOF>> {
- if (is_main_input_)
- {
- is_main_input_ = false;
+<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
+ reflect after.
+ */
+ is_main_input_ = include_stack_.size () > 2;
+ if (!close_input () || !is_main_input_)
/* Returns YY_NULL */
yyterminate ();
}
}
<INITIAL>{
- {DASHED_WORD} {
- return scan_bare_word (YYText ());
+ {WORD}/[-_] | // backup rule
+ {WORD} {
+ return scan_bare_word (YYText_utf8 ());
}
- {DASHED_KEY_WORD} {
- return scan_escaped_word (YYText () + 1);
+ {COMMAND}/[-_] | // backup rule
+ {COMMAND} {
+ return scan_escaped_word (YYText_utf8 () + 1);
}
}
-{WORD} {
- return scan_bare_word (YYText ());
-}
-{KEYWORD} {
- return scan_escaped_word (YYText () + 1);
+{FRACTION} {
+ yylval.scm = scan_fraction (YYText ());
+ return FRACTION;
}
-{REAL} {
- Real r;
- int cnv = sscanf (YYText (), "%lf", &r);
- assert (cnv == 1);
- (void) cnv;
- yylval.scm = scm_from_double (r);
+-{UNSIGNED} | // backup rule
+{REAL} {
+ yylval.scm = scm_c_read_string (YYText ());
return REAL;
}
+{UNSIGNED}/\/ | // backup rule
{UNSIGNED} {
- yylval.i = String_convert::dec2int (string (YYText ()));
+ yylval.scm = scm_c_read_string (YYText ());
return UNSIGNED;
}
return YYText ()[0];
}
+
+-/\. | // backup rule
[*:=] {
char c = YYText ()[0];
}
}
-<*>. {
- 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
-Lily_lexer::push_chord_state (SCM tab)
+Lily_lexer::push_chord_state (SCM alist)
{
- pitchname_tab_stack_ = scm_cons (tab, pitchname_tab_stack_);
+ SCM p = scm_assq (alist, pitchname_tab_stack_);
+
+ if (scm_is_false (p))
+ p = scm_cons (alist, alist_to_hashq (alist));
+ pitchname_tab_stack_ = scm_cons (p, pitchname_tab_stack_);
yy_push_state (chords);
}
}
void
-Lily_lexer::push_note_state (SCM tab)
+Lily_lexer::push_note_state (SCM alist)
{
- pitchname_tab_stack_ = scm_cons (tab, pitchname_tab_stack_);
+ bool extra = (YYSTATE == extratoken);
+
+ SCM p = scm_assq (alist, pitchname_tab_stack_);
+
+ if (extra)
+ yy_pop_state ();
+
+ if (scm_is_false (p))
+ p = scm_cons (alist, alist_to_hashq (alist));
+ pitchname_tab_stack_ = scm_cons (p, pitchname_tab_stack_);
yy_push_state (notes);
+
+ if (extra) {
+ hidden_state_ = YYSTATE;
+ yy_push_state (extratoken);
+ }
}
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))
{
- yylval.scm = get_music_function_transform (sid);
+ int funtype = SCM_FUNCTION;
- 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))
+ yylval.scm = sid;
+
+ SCM s = get_music_function_signature (sid);
+ 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 = scm_makfrom0str (str.c_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
if ((YYSTATE == notes) || (YYSTATE == chords)) {
SCM handle = SCM_BOOL_F;
if (scm_is_pair (pitchname_tab_stack_))
- handle = scm_hashq_get_handle (scm_car (pitchname_tab_stack_), sym);
+ handle = scm_hashq_get_handle (scm_cdar (pitchname_tab_stack_), sym);
if (scm_is_pair (handle)) {
yylval.scm = scm_cdr (handle);
else if (scm_is_symbol (yylval.scm))
return DRUM_PITCH;
}
- else if ((handle = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
+ else if ((YYSTATE == chords)
+ && (handle = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
{
yylval.scm = scm_cdr (handle);
return CHORD_MODIFIER;
}
}
-
- yylval.scm = scm_makfrom0str (str.c_str ());
+ yylval.scm = ly_string2scm (str);
return STRING;
}
return get_state () == figures;
}
+// The extra_token parameter specifies how to convert multiple values
+// into additional tokens. For '#', additional values get pushed as
+// SCM_IDENTIFIER. For '$', they get checked for their type and get
+// pushed as a corresponding *_IDENTIFIER token. Since the latter
+// tampers with yylval, it can only be done from the lexer itself, so
+// this function is private.
+
+SCM
+Lily_lexer::eval_scm (SCM readerdata, char extra_token)
+{
+ 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;
+ }
+
+ if (extra_token && SCM_VALUESP (sval))
+ {
+ sval = scm_struct_ref (sval, SCM_INUM0);
+
+ if (scm_is_pair (sval)) {
+ for (SCM v = scm_reverse (scm_cdr (sval));
+ scm_is_pair (v);
+ v = scm_cdr (v))
+ {
+ int token;
+ switch (extra_token) {
+ case '$':
+ token = scan_scm_id (scm_car (v));
+ if (!scm_is_eq (yylval.scm, SCM_UNSPECIFIED))
+ push_extra_token (token, yylval.scm);
+ break;
+ case '#':
+ push_extra_token (SCM_IDENTIFIER, scm_car (v));
+ break;
+ }
+ }
+ sval = scm_car (sval);
+ } else
+ sval = 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
{
Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
Lilypond_version ver (s);
- if (! ((ver >= oldest_version) && (ver <= current)))
+ if (int (ver) < oldest_version)
{
- non_fatal_error (_f ("Incorrect lilypond version: %s (%s, %s)", ver.to_string (), oldest_version.to_string (), current.to_string ()));
- non_fatal_error (_ ("Consider updating the input with the convert-ly script"));
+ non_fatal_error (_f ("file too old: %s (oldest supported: %s)", ver.to_string (), oldest_version.to_string ()));
+ non_fatal_error (_ ("consider updating the input with the convert-ly script"));
return false;
- }
+ }
+
+ if (ver > current)
+ {
+ non_fatal_error (_f ("program too old: %s (file requires: %s)", current.to_string (), ver.to_string ()));
+ return false;
+ }
return true;
}
/*
- substitute _ and \,
+ substitute _
*/
string
lyric_fudge (string s)
{
- char *chars = string_copy (s);
-
- 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);
- }
+ size_t i=0;
- return s;
+ while ((i = s.find ('_', i)) != string::npos)
+ {
+ s[i++] = ' ';
+ }
+ return s;
}
/*
lookup_markup_command (string s)
{
SCM proc = ly_lily_module_constant ("lookup-markup-command");
- return scm_call_1 (proc, scm_makfrom0str (s.c_str ()));
+ return scm_call_1 (proc, ly_string2scm (s));
+}
+
+SCM
+lookup_markup_list_command (string s)
+{
+ SCM proc = ly_lily_module_constant ("lookup-markup-list-command");
+ return scm_call_1 (proc, ly_string2scm (s));
}
/* Shut up lexer warnings. */