]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/lexer.ll
Replace WORD token in parser and lexer with SYMBOL
[lilypond.git] / lily / lexer.ll
index 6b19c8f67dd8aed1d205736a1fe8a182afb5af78..93184ebe085dc2d432274a06d4eeb887e565fe01 100644 (file)
@@ -1,8 +1,8 @@
-%{ // -*-Fundamental-*-
+%{ // -*- mode: c++; c-file-style: "linux"; indent-tabs-mode: t -*-
 /*
   This file is part of LilyPond, the GNU music typesetter.
 
 /*
   This file is part of LilyPond, the GNU music typesetter.
 
-  Copyright (C) 1996--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  Copyright (C) 1996--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
                  Jan Nieuwenhuizen <janneke@gnu.org>
 
   LilyPond is free software: you can redistribute it and/or modify
                  Jan Nieuwenhuizen <janneke@gnu.org>
 
   LilyPond is free software: you can redistribute it and/or modify
   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
   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
 
 /*
   backup rules
 
-  after making a change to the lexer rules, run 
+  after making a change to the lexer rules, run
       flex -b <this lexer file>
       flex -b <this lexer file>
-  and make sure that 
+  and make sure that
       lex.backup
   contains no backup states, but only the reminder
       Compressed tables always back up.
       lex.backup
   contains no backup states, but only the reminder
       Compressed tables always back up.
@@ -47,7 +55,6 @@ using namespace std;
 
 #include "context-def.hh"
 #include "duration.hh"
 
 #include "context-def.hh"
 #include "duration.hh"
-#include "identifier-smob.hh"
 #include "international.hh"
 #include "interval.hh"
 #include "lily-guile.hh"
 #include "international.hh"
 #include "interval.hh"
 #include "lily-guile.hh"
@@ -62,9 +69,9 @@ using namespace std;
 #include "pitch.hh"
 #include "source-file.hh"
 #include "std-string.hh"
 #include "pitch.hh"
 #include "source-file.hh"
 #include "std-string.hh"
-#include "string-convert.hh"
 #include "version.hh"
 #include "warn.hh"
 #include "version.hh"
 #include "warn.hh"
+#include "lily-imports.hh"
 
 /*
 RH 7 fix (?)
 
 /*
 RH 7 fix (?)
@@ -79,19 +86,23 @@ SCM lookup_markup_list_command (string s);
 bool is_valid_version (string s);
 
 
 bool is_valid_version (string s);
 
 
-#define start_quote()  \
-       yy_push_state (quote);\
-       yylval.string = new string
+#define start_quote() do {                      \
+                yy_push_state (quote);          \
+                yylval = SCM_EOL;               \
+        } while (0)
 
 
-#define start_lyric_quote()    \
-       yy_push_state (lyric_quote);\
-       yylval.string = new string
+/*
+  The inside of \"violin1" is marked by commandquote mode
+*/
 
 
-#define yylval \
-       (*(YYSTYPE*)lexval_)
+#define start_command_quote() do {             \
+                yy_push_state (commandquote);  \
+                yylval = SCM_EOL;               \
+        } while (0)
 
 
-#define yylloc \
-       (*(YYLTYPE*)lexloc_)
+#define yylval (*lexval_)
+
+#define yylloc (*lexloc_)
 
 #define YY_USER_ACTION add_lexed_char (YYLeng ());
 
 
 #define YY_USER_ACTION add_lexed_char (YYLeng ());
 
@@ -109,51 +120,60 @@ SCM (* scm_parse_error_handler) (void *);
 %option debug
 %option yyclass="Lily_lexer"
 %option stack
 %option debug
 %option yyclass="Lily_lexer"
 %option stack
-%option never-interactive 
+%option never-interactive
 %option warn
 
 %option warn
 
-%x extratoken
 %x chords
 %x figures
 %x incl
 %x lyrics
 %x chords
 %x figures
 %x incl
 %x lyrics
-%x lyric_quote
 %x longcomment
 %x longcomment
+%x maininput
 %x markup
 %x notes
 %x quote
 %x markup
 %x notes
 %x quote
+%x commandquote
 %x sourcefileline
 %x sourcefilename
 %x version
 
 %x sourcefileline
 %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]
 A              [a-zA-Z\200-\377]
 AA             {A}|_
 N              [0-9]
-AN             {AA}|{N}
 ANY_CHAR       (.|\n)
 ANY_CHAR       (.|\n)
-PUNCT          [?!:'`]
-ACCENT         \\[`'"^]
-NATIONAL       [\001-\006\021-\027\031\036]
-TEX            {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
-DASHED_WORD            {A}({AN}|-)*
-DASHED_KEY_WORD                \\{DASHED_WORD}
-
-
-
-ALPHAWORD      {A}+
+SYMBOL         {A}([-_]{A}|{A})*
+COMMAND                \\{SYMBOL}
+/* SPECIAL category is for every letter that needs to get passed to
+ * the parser rather than being redefinable by the user */
+SPECIAL                [-+*/=<>{}!?_^'',.:]
+SHORTHAND      (.|\\.)
 UNSIGNED       {N}+
 E_UNSIGNED     \\{N}+
 FRACTION       {N}+\/{N}+
 INT            -?{UNSIGNED}
 REAL           ({INT}\.{N}*)|(-?\.{N}+)
 UNSIGNED       {N}+
 E_UNSIGNED     \\{N}+
 FRACTION       {N}+\/{N}+
 INT            -?{UNSIGNED}
 REAL           ({INT}\.{N}*)|(-?\.{N}+)
+STRICTREAL      {UNSIGNED}\.{UNSIGNED}
 WHITE          [ \n\t\f\r]
 HORIZONTALWHITE                [ \t]
 BLACK          [^ \n\t\f\r]
 RESTNAME       [rs]
 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\\'"]
+ESCAPED                [nt\\''""]
 EXTENDER       __
 HYPHEN         --
 BOM_UTF8       \357\273\277
 EXTENDER       __
 HYPHEN         --
 BOM_UTF8       \357\273\277
@@ -165,38 +185,10 @@ BOM_UTF8  \357\273\277
        // swallow and ignore carriage returns
 }
 
        // swallow and ignore carriage returns
 }
 
-<extratoken>{ANY_CHAR} {
-  /* Generate a token without swallowing anything */
-
-  /* First unswallow the eaten character */
-  add_lexed_char (-YYLeng ());
-  yyless (0);
-
-  /* produce requested token */
-  int type = extra_token_types_.back ();
-  extra_token_types_.pop_back ();
-  if (extra_token_types_.empty ())
-    yy_pop_state ();
-
-  return type;
-}
-
-<extratoken><<EOF>>    {
-  /* Generate a token without swallowing anything */
-
-  /* produce requested token */
-  int type = extra_token_types_.back ();
-  extra_token_types_.pop_back ();
-  if (extra_token_types_.empty ())
-    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}/.* {
    /* 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 (lexloc_->line_number () != 1 || lexloc_->column_number () != 0)
     {
       LexerWarning (_ ("stray UTF-8 BOM encountered").c_str ());
       // exit (1);
     {
       LexerWarning (_ ("stray UTF-8 BOM encountered").c_str ());
       // exit (1);
@@ -208,13 +200,10 @@ BOM_UTF8  \357\273\277
   "%{" {
        yy_push_state (longcomment);
   }
   "%{" {
        yy_push_state (longcomment);
   }
-  %[^{\n\r][^\n\r]*[\n\r]      {
+  %[^{\n\r][^\n\r]*[\n\r]?     {
+         (void) YYText_utf8 ();
   }
   }
-  %[^{\n\r]    { // backup rule
-  }
-  %[\n\r]      {
-  }
-  %[^{\n\r][^\n\r]*    {
+  %[\n\r]?     {
   }
   {WHITE}+     {
 
   }
   {WHITE}+     {
 
@@ -236,8 +225,8 @@ BOM_UTF8    \357\273\277
 <INITIAL,chords,lyrics,notes,figures>\\sourcefileline{WHITE}*  {
        yy_push_state (sourcefileline);
 }
 <INITIAL,chords,lyrics,notes,figures>\\sourcefileline{WHITE}*  {
        yy_push_state (sourcefileline);
 }
-<version>\"[^"]*\"     { /* got the version number */
-       string s (YYText () + 1);
+<version>\"[^""]*\"     { /* got the version number */
+       string s (YYText_utf8 () + 1);
        s = s.substr (0, s.rfind ('\"'));
 
        yy_pop_state ();
        s = s.substr (0, s.rfind ('\"'));
 
        yy_pop_state ();
@@ -245,17 +234,17 @@ BOM_UTF8  \357\273\277
        SCM top_scope = scm_car (scm_last_pair (scopes_));
        scm_module_define (top_scope, ly_symbol2scm ("version-seen"), SCM_BOOL_T);
 
        SCM top_scope = scm_car (scm_last_pair (scopes_));
        scm_module_define (top_scope, ly_symbol2scm ("version-seen"), SCM_BOOL_T);
 
-       if (!is_valid_version (s))
+       if (!is_valid_version (s)) {
+                yylval = SCM_UNSPECIFIED;
                return INVALID;
                return INVALID;
-
-
+        }
 }
 }
-<sourcefilename>\"[^"]*\"     {
-       string s (YYText () + 1);
+<sourcefilename>\"[^""]*\"     {
+       string s (YYText_utf8 () + 1);
        s = s.substr (0, s.rfind ('\"'));
 
        yy_pop_state ();
        s = s.substr (0, s.rfind ('\"'));
 
        yy_pop_state ();
-       this->here_input().get_source_file ()->name_ = s;
+       here_input().get_source_file ()->name_ = s;
        message (_f ("Renaming input to: `%s'", s.c_str ()));
        progress_indication ("\n");
        scm_module_define (scm_car (scopes_),
        message (_f ("Renaming input to: `%s'", s.c_str ()));
        progress_indication ("\n");
        scm_module_define (scm_car (scopes_),
@@ -269,7 +258,7 @@ BOM_UTF8    \357\273\277
        sscanf (YYText (), "%d", &i);
 
        yy_pop_state ();
        sscanf (YYText (), "%d", &i);
 
        yy_pop_state ();
-       this->here_input ().get_source_file ()->set_line (here_input ().start (), i);
+       here_input ().get_source_file ()->set_line (here_input ().start (), i);
 }
 
 <version>{ANY_CHAR}    {
 }
 
 <version>{ANY_CHAR}    {
@@ -286,9 +275,10 @@ BOM_UTF8   \357\273\277
 }
 <longcomment>{
        [^\%]*          {
 }
 <longcomment>{
        [^\%]*          {
+               (void) YYText_utf8 ();
        }
        \%*[^}%]*               {
        }
        \%*[^}%]*               {
-
+               (void) YYText_utf8 ();
        }
        "%"+"}"         {
                yy_pop_state ();
        }
        "%"+"}"         {
                yy_pop_state ();
@@ -300,24 +290,28 @@ BOM_UTF8  \357\273\277
        if (!is_main_input_)
        {
                start_main_input ();
        if (!is_main_input_)
        {
                start_main_input ();
+               main_input_level_ = include_stack_.size ();
                is_main_input_ = true;
                is_main_input_ = true;
+               int state = YYSTATE;
+               yy_push_state (maininput);
+               yy_push_state (state);
        }
        else
        }
        else
-               error (_ ("\\maininput not allowed outside init files"));
+               LexerError (_ ("\\maininput not allowed outside init files").c_str ());
 }
 
 <INITIAL,chords,lyrics,figures,notes>\\include           {
        yy_push_state (incl);
 }
 }
 
 <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 */
        s = s.substr (0, s.rfind ('"'));
 
        new_input (s, sources_);
        yy_pop_state ();
 }
 <incl>\\{BLACK}*{WHITE}? { /* got the include identifier */
-       string s = YYText () + 1;
+       string s = YYText_utf8 () + 1;
        strip_trailing_white (s);
        if (s.length () && (s[s.length () - 1] == ';'))
          s = s.substr (0, s.length () - 1);
        strip_trailing_white (s);
        if (s.length () && (s[s.length () - 1] == ';'))
          s = s.substr (0, s.length () - 1);
@@ -326,7 +320,7 @@ BOM_UTF8    \357\273\277
        if (scm_is_string (sid)) {
                new_input (ly_scm2string (sid), sources_);
                yy_pop_state ();
        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 ());
            string msg (_f ("wrong or undefined identifier: `%s'", s ));
 
            LexerError (msg.c_str ());
@@ -335,215 +329,327 @@ BOM_UTF8        \357\273\277
            scm_display (sid, err);
          }
 }
            scm_display (sid, err);
          }
 }
-<incl,version,sourcefilename>\"[^"]*   { // backup rule
-       error (_ ("end quote missing"));
-       exit (1);
+<incl>(\$|#) { // scm for the filename
+       Input hi = here_input();
+       hi.step_forward ();
+       SCM sval = ly_parse_scm (hi, be_safe_global && is_main_input_, parser_);
+       sval = eval_scm (sval, hi);
+       int n = hi.end () - hi.start ();
+
+       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 (!SCM_UNBNDP (sval)) {
+                       SCM err = scm_current_error_port ();
+                       scm_puts ("This value was found instead: ", err);
+                       scm_display (sval, err);
+               }
+       }
+}
+
+<incl,version,sourcefilename>\"[^""]*   { // backup rule
+       LexerError (_ ("end quote missing").c_str ());
+       yy_pop_state ();
 }
 }
+
+    /* Flex picks the longest matching pattern including trailing
+     * contexts.  Without the backup pattern, r-. does not trigger the
+     * {RESTNAME} rule but rather the {SYMBOL}/[-_] rule coming later,
+     * needed for avoiding backup states.
+     */
+
+<chords,notes,figures>{RESTNAME}/[-_]  |  // pseudo backup rule
 <chords,notes,figures>{RESTNAME}       {
        char const *s = YYText ();
 <chords,notes,figures>{RESTNAME}       {
        char const *s = YYText ();
-       yylval.scm = scm_from_locale_string (s);
+       yylval = scm_from_ascii_string (s);
        return RESTNAME;
 }
        return RESTNAME;
 }
+<chords,notes,figures>q/[-_]   | // pseudo backup rule
+<chords,notes,figures>q        {
+        yylval = SCM_UNSPECIFIED;
+       return CHORD_REPETITION;
+}
+
+<chords,notes,figures>R/[-_]   | // pseudo backup rule
 <chords,notes,figures>R                {
 <chords,notes,figures>R                {
+        yylval = SCM_UNSPECIFIED;
        return MULTI_MEASURE_REST;
 }
 <INITIAL,chords,figures,lyrics,markup,notes>#  { //embedded scm
        return MULTI_MEASURE_REST;
 }
 <INITIAL,chords,figures,lyrics,markup,notes>#  { //embedded scm
-       int n = 0;
        Input hi = here_input();
        hi.step_forward ();
        Input hi = here_input();
        hi.step_forward ();
-       SCM sval = ly_parse_scm (hi.start (), &n, hi,
-               be_safe_global && is_main_input_, parser_);
+       SCM sval = ly_parse_scm (hi, be_safe_global && is_main_input_, parser_);
 
 
-       if (sval == SCM_UNDEFINED)
-       {
-               sval = SCM_UNSPECIFIED;
+       if (SCM_UNBNDP (sval))
                error_level_ = 1;
                error_level_ = 1;
-       }
 
 
+       int n = hi.end () - hi.start ();
        for (int i = 0; i < n; i++)
        {
                yyinput ();
        }
        char_count_stack_.back () += n;
 
        for (int i = 0; i < n; i++)
        {
                yyinput ();
        }
        char_count_stack_.back () += n;
 
-       if (unpack_identifier (sval) != SCM_UNDEFINED)
+       yylval = sval;
+       return SCM_TOKEN;
+}
+
+<INITIAL,chords,figures,lyrics,markup,notes>\$ { //immediate scm
+       Input hi = here_input();
+       hi.step_forward ();
+       SCM sval = ly_parse_scm (hi, be_safe_global && is_main_input_, parser_);
+
+       int n = hi.end () - hi.start ();
+
+       for (int i = 0; i < n; i++)
        {
        {
-               yylval.scm = unpack_identifier(sval);
-               return identifier_type (yylval.scm);
+               yyinput ();
        }
        }
+       char_count_stack_.back () += n;
 
 
-       for (size_t i = 0; i < pending_string_includes_.size (); i++)
-               new_input ("<included string>", pending_string_includes_[i],
-                          parser_->sources_);
-       pending_string_includes_.clear ();
-               
-       yylval.scm = sval;
-       return SCM_TOKEN;
+       sval = eval_scm (sval, hi, '$');
+
+       int token = scan_scm_id (sval);
+       if (!scm_is_eq (yylval, SCM_UNSPECIFIED))
+               return token;
 }
 }
-<INITIAL,notes,lyrics>{ 
+
+<INITIAL,notes,lyrics,chords>{
        \<\<    {
        \<\<    {
+                yylval = SCM_UNSPECIFIED;
                return DOUBLE_ANGLE_OPEN;
        }
        \>\>    {
                return DOUBLE_ANGLE_OPEN;
        }
        \>\>    {
+                yylval = SCM_UNSPECIFIED;
                return DOUBLE_ANGLE_CLOSE;
        }
 }
 
                return DOUBLE_ANGLE_CLOSE;
        }
 }
 
-<INITIAL,notes>{
+<INITIAL,notes,chords>{
        \<      {
        \<      {
+                yylval = SCM_UNSPECIFIED;
                return ANGLE_OPEN;
        }
        \>      {
                return ANGLE_OPEN;
        }
        \>      {
+                yylval = SCM_UNSPECIFIED;
                return ANGLE_CLOSE;
        }
 }
 
 <figures>{
        _       {
                return ANGLE_CLOSE;
        }
 }
 
 <figures>{
        _       {
+                yylval = SCM_UNSPECIFIED;
                return FIGURE_SPACE;
        }
        \>              {
                return FIGURE_SPACE;
        }
        \>              {
+                yylval = SCM_UNSPECIFIED;
                return FIGURE_CLOSE;
        }
        \<      {
                return FIGURE_CLOSE;
        }
        \<      {
+                yylval = SCM_UNSPECIFIED;
                return FIGURE_OPEN;
        }
                return FIGURE_OPEN;
        }
+       \\\+    {
+               yylval = SCM_UNSPECIFIED;
+               return E_PLUS;
+       }
+       \\!     {
+               yylval = SCM_UNSPECIFIED;
+               return E_EXCLAMATION;
+       }
+       \\\\    {
+               yylval = SCM_UNSPECIFIED;
+               return E_BACKSLASH;
+       }
+       [][]    {
+               yylval = SCM_UNSPECIFIED;
+               return  YYText ()[0];
+       }
 }
 
 <notes,figures>{
 }
 
 <notes,figures>{
-       {ALPHAWORD}     {
-               return scan_bare_word (YYText ());
+       {SYMBOL}/[-_]   | // backup rule
+       {SYMBOL}        {
+               return scan_bare_word (YYText_utf8 ());
        }
        }
-
-       {NOTECOMMAND}   {
-               return scan_escaped_word (YYText () + 1); 
+       \\\"    {
+               start_command_quote ();
+       }
+       {COMMAND}/[-_]  | // backup rule
+       {COMMAND}       {
+               return scan_escaped_word (YYText_utf8 () + 1); 
        }
        {FRACTION}      {
        }
        {FRACTION}      {
-               yylval.scm =  scan_fraction (YYText ());
+               yylval =  scan_fraction (YYText ());
                return FRACTION;
        }
                return FRACTION;
        }
-       {UNSIGNED}/\/   | // backup rule
-       {UNSIGNED}              {
-               yylval.i = String_convert::dec2int (string (YYText ()));
+       {STRICTREAL}    {
+               yylval = scm_c_read_string (YYText ());
+               return REAL;
+       }
+       {UNSIGNED}/[/.] | // backup rule
+       {UNSIGNED}      {
+               yylval = scm_c_read_string (YYText ());
                return UNSIGNED;
        }
        {E_UNSIGNED}    {
                return UNSIGNED;
        }
        {E_UNSIGNED}    {
-               yylval.i = String_convert::dec2int (string (YYText () +1));
+               yylval = scm_c_read_string (YYText () + 1);
                return E_UNSIGNED;
        }
 }
 
                return E_UNSIGNED;
        }
 }
 
-<quote,lyric_quote>{
+<quote,commandquote>{
        \\{ESCAPED}     {
        \\{ESCAPED}     {
-               *yylval.string += to_string (escaped_char (YYText ()[1]));
+                char c = escaped_char (YYText ()[1]);
+               yylval = scm_cons (scm_from_ascii_stringn (&c, 1),
+                                   yylval);
        }
        [^\\""]+        {
        }
        [^\\""]+        {
-               *yylval.string += YYText ();
+                yylval = scm_cons (scm_from_utf8_string (YYText_utf8 ()),
+                                   yylval);
        }
        \"      {
 
        }
        \"      {
 
+               /* yylval is union. Must remember STRING before setting SCM*/
+
+                yylval = scm_string_concatenate_reverse (yylval,
+                                                         SCM_UNDEFINED,
+                                                         SCM_UNDEFINED);
+
+               if (get_state () == commandquote) {
+                       yy_pop_state ();
+                       return scan_escaped_word (ly_scm2string (yylval));
+               }
+
                yy_pop_state ();
 
                yy_pop_state ();
 
-               /* yylval is union. Must remember STRING before setting SCM*/
-               string *sp = yylval.string;
-               yylval.scm = ly_string2scm (*sp);
-               delete sp;
-               return is_lyric_state () ? LYRICS_STRING : STRING;
+               return STRING;
        }
        }
-       .       {
-               *yylval.string += YYText ();
+       \\      {
+                yylval = scm_cons (scm_from_ascii_string (YYText ()),
+                                   yylval);
        }
 }
 
 <lyrics>{
        \" {
        }
 }
 
 <lyrics>{
        \" {
-               start_lyric_quote ();
+               start_quote ();
        }
        {FRACTION}      {
        }
        {FRACTION}      {
-               yylval.scm =  scan_fraction (YYText ());
+               yylval =  scan_fraction (YYText ());
                return FRACTION;
        }
                return FRACTION;
        }
-       {UNSIGNED}/\/[^0-9] { // backup rule
-               yylval.i = String_convert::dec2int (string (YYText ()));
-               return UNSIGNED;
+       {STRICTREAL}    {
+               yylval = scm_c_read_string (YYText ());
+               return REAL;
        }
        }
-       {UNSIGNED}/\/   | // backup rule
+       {UNSIGNED}/[/.] | // backup rule
        {UNSIGNED}              {
        {UNSIGNED}              {
-               yylval.i = String_convert::dec2int (string (YYText ()));
+               yylval = scm_c_read_string (YYText ());
                return UNSIGNED;
        }
                return UNSIGNED;
        }
-       {NOTECOMMAND}   {
-               return scan_escaped_word (YYText () + 1);
+       \\\"    {
+               start_command_quote ();
+       }
+       {COMMAND}/[-_]  | // backup rule
+       {COMMAND}       {
+               return scan_escaped_word (YYText_utf8 () + 1);
+       }
+       \\.|\|  {
+               // UTF-8 already covered by COMMAND
+               return scan_shorthand (YYText ());
+       }
+       /* Characters needed to express durations, assignments */
+       [*.=]   {
+                yylval = SCM_UNSPECIFIED;
+               return YYText ()[0];
        }
        }
-       {LYRICS} {
+       [^|*.=$#{}\"\\ \t\n\r\f0-9][^$#{}\"\\ \t\n\r\f0-9]* {
                /* ugr. This sux. */
                /* ugr. This sux. */
-               string s (YYText ()); 
+               string s (YYText_utf8 ());
+                yylval = SCM_UNSPECIFIED;
                if (s == "__")
                if (s == "__")
-                       return yylval.i = EXTENDER;
+                       return EXTENDER;
                if (s == "--")
                if (s == "--")
-                       return yylval.i = HYPHEN;
+                       return HYPHEN;
                s = lyric_fudge (s);
                s = lyric_fudge (s);
+               yylval = ly_string2scm (s);
 
 
-               char c = s[s.length () - 1];
-               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 = ly_string2scm (s);
-
-
-               return LYRICS_STRING;
+               return SYMBOL;
        }
        }
-       . {
+       /* This should really just cover {} */
+       [{}] {
+                yylval = SCM_UNSPECIFIED;
                return YYText ()[0];
        }
 }
 <chords>{
                return YYText ()[0];
        }
 }
 <chords>{
-       {ALPHAWORD}     {
-               return scan_bare_word (YYText ());
+       {SYMBOL}/[-_]   | // backup rule
+       {SYMBOL}        {
+               return scan_bare_word (YYText_utf8 ());
+       }
+       \\\"    {
+               start_command_quote ();
        }
        }
-       {NOTECOMMAND}   {
-               return scan_escaped_word (YYText () + 1);
+       {COMMAND}/[-_]  | // backup rule
+       {COMMAND}       {
+               return scan_escaped_word (YYText_utf8 () + 1);
        }
        {FRACTION}      {
        }
        {FRACTION}      {
-               yylval.scm =  scan_fraction (YYText ());
+               yylval =  scan_fraction (YYText ());
                return FRACTION;
        }
                return FRACTION;
        }
-       {UNSIGNED}/\/[^0-9] { // backup rule
-               yylval.i = String_convert::dec2int (string (YYText ()));
-               return UNSIGNED;
-       }
        {UNSIGNED}/\/   | // backup rule
        {UNSIGNED}              {
        {UNSIGNED}/\/   | // backup rule
        {UNSIGNED}              {
-               yylval.i = String_convert::dec2int (string (YYText ()));
+               yylval = scm_c_read_string (YYText ());
                return UNSIGNED;
        }
        -  {
                return UNSIGNED;
        }
        -  {
+                yylval = SCM_UNSPECIFIED;
                return CHORD_MINUS;
        }
        :  {
                return CHORD_MINUS;
        }
        :  {
+                yylval = SCM_UNSPECIFIED;
                return CHORD_COLON;
        }
        \/\+ {
                return CHORD_COLON;
        }
        \/\+ {
+                yylval = SCM_UNSPECIFIED;
                return CHORD_BASS;
        }
        \/  {
                return CHORD_BASS;
        }
        \/  {
+                yylval = SCM_UNSPECIFIED;
                return CHORD_SLASH;
        }
        \^  {
                return CHORD_SLASH;
        }
        \^  {
+                yylval = SCM_UNSPECIFIED;
                return CHORD_CARET;
        }
                return CHORD_CARET;
        }
-       . {
-               return YYText ()[0];
-       }
 }
 
 
 <markup>{
        \\score {
 }
 
 
 <markup>{
        \\score {
+                yylval = SCM_UNSPECIFIED;
                return SCORE;
        }
                return SCORE;
        }
-       {MARKUPCOMMAND} {
-               string str (YYText () + 1);
+       \\score-lines {
+               yylval = SCM_UNSPECIFIED;
+               return SCORELINES;
+       }
+       \\\"    {
+               start_command_quote ();
+       }
+       {COMMAND}/[-_]  | // backup rule
+       {COMMAND} {
+               string str (YYText_utf8 () + 1);
 
                 int token_type = MARKUP_FUNCTION;
                SCM s = lookup_markup_command (str);
 
                 int token_type = MARKUP_FUNCTION;
                SCM s = lookup_markup_command (str);
@@ -572,62 +678,68 @@ BOM_UTF8  \357\273\277
                // in reverse order, so the first token pushed in the
                // loop will be EXPECT_NO_MORE_ARGS.
 
                // in reverse order, so the first token pushed in the
                // loop will be EXPECT_NO_MORE_ARGS.
 
-               yylval.scm = scm_car(s);
+               yylval = scm_car(s);
 
                // yylval now contains the function to call as token
                // value (for token type MARKUP_FUNCTION or
                // MARKUP_LIST_FUNCTION).
 
 
                // 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);
+               push_extra_token (here_input (), EXPECT_NO_MORE_ARGS);
                s = scm_cdr(s);
                for (; scm_is_pair(s); s = scm_cdr(s)) {
                  SCM predicate = scm_car(s);
 
                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);
+                 if (scm_is_eq (predicate, SCM (Lily::markup_list_p)))
+                   push_extra_token (here_input (), EXPECT_MARKUP_LIST);
+                 else if (scm_is_eq (predicate, SCM (Lily::markup_p)))
+                   push_extra_token (here_input (), EXPECT_MARKUP);
                  else
                  else
-                   push_extra_token(EXPECT_SCM);
+                   push_extra_token (here_input (), EXPECT_SCM, predicate);
                }
                return token_type;
        }
                }
                return token_type;
        }
-       [{}]    {
-               return YYText ()[0];
-       }
-       [^#{}\"\\ \t\n\r\f]+ {
-               string s (YYText ()); 
-
-               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 = ly_string2scm (s);
-
+       [^$#{}\"\\ \t\n\r\f]+ {
+               string s (YYText_utf8 ()); 
 
 
-               return STRING;
+               yylval = ly_string2scm (s);
+               return SYMBOL;
        }
        }
-       .  {
-               return YYText()[0];
+       [{}]  {
+                yylval = SCM_UNSPECIFIED;
+               return YYText ()[0];
        }
 }
 
 <longcomment><<EOF>> {
                LexerError (_ ("EOF found inside a comment").c_str ());
        }
 }
 
 <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
+               yy_pop_state ();
        }
 
        }
 
-<<EOF>> { if (is_main_input_)
+<quote,commandquote><<EOF>> {
+       LexerError (_ ("EOF found inside string").c_str ());
+       yy_pop_state ();
+}
+
+<<EOF>> {
+        yylval = SCM_UNSPECIFIED;
+        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_ = include_stack_.size () > main_input_level_;
+               if (!is_main_input_)
+               {
+                       main_input_level_ = 0;
+                       pop_state ();
+                       if (YYSTATE != maininput)
+                       {
+                               LexerError (_ ("Unfinished main input").c_str ());
+                               do {
+                                       yy_pop_state ();
+                               } while (YYSTATE != maininput);
+                       }
+                       extra_tokens_ = SCM_EOL;
+                       yy_pop_state ();
+               }
+               if (!close_input () || !is_main_input_)
                /* Returns YY_NULL */
                        yyterminate ();
        }
                /* Returns YY_NULL */
                        yyterminate ();
        }
@@ -636,106 +748,100 @@ BOM_UTF8        \357\273\277
                yyterminate ();
 }
 
                yyterminate ();
 }
 
+<maininput>{ANY_CHAR} {
+       while (include_stack_.size () > main_input_level_
+              && close_input ())
+               ;
+       yyterminate ();
+}
+
 <INITIAL>{
 <INITIAL>{
-       {DASHED_WORD}   {
-               return scan_bare_word (YYText ());
+       {SYMBOL}/[-_]   | // backup rule
+       {SYMBOL}        {
+               return scan_bare_word (YYText_utf8 ());
        }
        }
-       {DASHED_KEY_WORD}       {
-               return scan_escaped_word (YYText () + 1);
+       \\\"    {
+               start_command_quote ();
        }
        }
+       {COMMAND}/[-_]  | // backup rule
+       {COMMAND}       {
+               return scan_escaped_word (YYText_utf8 () + 1);
+       }
+}
+
+{FRACTION}     {
+       yylval =  scan_fraction (YYText ());
+       return FRACTION;
 }
 
 -{UNSIGNED}    | // backup rule
 {REAL}         {
 }
 
 -{UNSIGNED}    | // backup rule
 {REAL}         {
-       Real r;
-       int cnv = sscanf (YYText (), "%lf", &r);
-       assert (cnv == 1);
-       (void) cnv;
-
-       yylval.scm = scm_from_double (r);
-       return REAL;
-}
--\.    { // backup rule
-       yylval.scm = scm_from_double (0.0);
+       yylval = scm_c_read_string (YYText ());
        return REAL;
 }
 
        return REAL;
 }
 
+{UNSIGNED}/\/  | // backup rule
 {UNSIGNED}     {
 {UNSIGNED}     {
-       yylval.i = String_convert::dec2int (string (YYText ()));
+       yylval = scm_c_read_string (YYText ());
        return UNSIGNED;
 }
 
 
        return UNSIGNED;
 }
 
 
-[{}]   {
-
+-/\.   { // backup rule
+        yylval = SCM_UNSPECIFIED;
        return YYText ()[0];
 }
        return YYText ()[0];
 }
-[*:=]          {
-       char c = YYText ()[0];
 
 
-       return c;
-}
-
-<INITIAL,notes,figures>.       {
+<INITIAL,chords,lyrics,figures,notes>{SPECIAL} {
+        yylval = SCM_UNSPECIFIED;
        return YYText ()[0];
 }
 
        return YYText ()[0];
 }
 
-<INITIAL,lyrics,notes,figures>\\. {
-    char c = YYText ()[1];
-
-    switch (c) {
-    case '>':
-       return E_ANGLE_CLOSE;
-    case '<':
-       return E_ANGLE_OPEN;
-    case '!':
-       return E_EXCLAMATION;
-    case '(':
-       return E_OPEN;
-    case ')':
-       return E_CLOSE;
-    case '[':
-       return E_BRACKET_OPEN;
-    case '+':
-       return E_PLUS;
-    case ']':
-       return E_BRACKET_CLOSE;
-    case '~':
-       return E_TILDE;
-    case '\\':
-       return E_BACKSLASH;
-
-    default:
-       return E_CHAR;
-    }
+<INITIAL,chords,lyrics,figures,notes>{SHORTHAND}       {
+       return scan_shorthand (YYText_utf8 ()); // should not be utf-8
 }
 
 }
 
-<*>.           {
-       string msg = _f ("invalid character: `%c'", YYText ()[0]);
+<*>.[\200-\277]*       {
+       string msg = _f ("invalid character: `%s'", YYText_utf8 ());
        LexerError (msg.c_str ());
        LexerError (msg.c_str ());
-       return YYText ()[0];
+        yylval = SCM_UNSPECIFIED;
+       return '%';  // Better not return half a utf8 character.
 }
 
 %%
 
 }
 
 %%
 
-/* Make the lexer generate a token of the given type as the next token. 
+/* 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
  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 (Input const &where, int token_type, SCM scm)
 {
 {
-       if (extra_token_types_.empty ())
-       {
-               if (YY_START != extratoken)
-                       hidden_state_ = YY_START;
-               yy_push_state (extratoken);
-       }
-       extra_token_types_.push_back (token_type);
+       extra_tokens_ = scm_cons (scm_cons2 (where.smobbed_copy (),
+                                            scm_from_int (token_type),
+                                            scm), extra_tokens_);
+}
+
+int
+Lily_lexer::pop_extra_token ()
+{
+       if (scm_is_null (extra_tokens_))
+               return -1;
+
+  /* produce requested token */
+       yylloc = *unsmob<Input> (scm_caar (extra_tokens_));
+       int type = scm_to_int (scm_cadar (extra_tokens_));
+       yylval = scm_cddar (extra_tokens_);
+       extra_tokens_ = scm_cdr (extra_tokens_);
+       return type;
 }
 
 void
 }
 
 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);
 }
 
        yy_push_state (chords);
 }
 
@@ -764,9 +870,13 @@ Lily_lexer::push_markup_state ()
 }
 
 void
 }
 
 void
-Lily_lexer::push_note_state (SCM tab)
+Lily_lexer::push_note_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 (notes);
 }
 
        yy_push_state (notes);
 }
 
@@ -776,38 +886,80 @@ Lily_lexer::pop_state ()
        if (YYSTATE == notes || YYSTATE == chords)
                pitchname_tab_stack_ = scm_cdr (pitchname_tab_stack_);
 
        if (YYSTATE == notes || YYSTATE == chords)
                pitchname_tab_stack_ = scm_cdr (pitchname_tab_stack_);
 
-       yy_pop_state ();
+       // don't cross the maininput threshold
+       if (YYSTATE != maininput)
+               yy_pop_state ();
+
 }
 
 int
 Lily_lexer::identifier_type (SCM sid)
 {
 }
 
 int
 Lily_lexer::identifier_type (SCM sid)
 {
-       int k = try_special_identifiers (&yylval.scm , sid);
+       int k = try_special_identifiers (&yylval , sid);
        return k >= 0  ? k : SCM_IDENTIFIER;
 }
 
 
 int
        return k >= 0  ? k : SCM_IDENTIFIER;
 }
 
 
 int
-Lily_lexer::scan_escaped_word (string str)
+Lily_lexer::scan_escaped_word (const string &str)
 {
        // use more SCM for this.
 
 //     SCM sym = ly_symbol2scm (str.c_str ());
 
 {
        // use more SCM for this.
 
 //     SCM sym = ly_symbol2scm (str.c_str ());
 
+        yylval = SCM_UNSPECIFIED;
        int i = lookup_keyword (str);
        int i = lookup_keyword (str);
-       if (i == MARKUP && is_lyric_state ())
-               return LYRIC_MARKUP;
+
        if (i != -1)
                return i;
 
        SCM sid = lookup_identifier (str);
        if (i != -1)
                return i;
 
        SCM sid = lookup_identifier (str);
-       if (is_music_function (sid))
+       if (Music *m = unsmob<Music> (sid))
+       {
+               m->set_spot (override_input (here_input ()));
+       }
+
+       if (!SCM_UNBNDP (sid))
+               return scan_scm_id (sid);
+
+       string msg (_f ("unknown escaped string: `\\%s'", str));
+       LexerError (msg.c_str ());
+
+       yylval = ly_string2scm (str);
+
+       return STRING; // SYMBOL would cause additional processing
+}
+
+int
+Lily_lexer::scan_shorthand (const string &str)
+{
+       SCM sid = lookup_identifier (str);
+       if (Music *m = unsmob<Music> (sid))
+       {
+               m->set_spot (override_input (here_input ()));
+       }
+
+       if (!SCM_UNBNDP (sid))
+               return scan_scm_id (sid);
+
+       string msg (_f ("undefined character or shorthand: %s", str));
+       LexerError (msg.c_str ());
+
+       yylval = ly_string2scm (str);
+
+       return STRING;
+}
+
+int
+Lily_lexer::scan_scm_id (SCM sid)
+{
+       if (Music_function *fun = unsmob<Music_function> (sid))
        {
                int funtype = SCM_FUNCTION;
 
        {
                int funtype = SCM_FUNCTION;
 
-               yylval.scm = get_music_function_transform (sid);
+               yylval = sid;
 
 
-               SCM s = scm_object_property (yylval.scm, ly_symbol2scm ("music-function-signature"));
+               SCM s = fun->get_signature ();
                SCM cs = scm_car (s);
 
                if (scm_is_pair (cs))
                SCM cs = scm_car (s);
 
                if (scm_is_pair (cs))
@@ -815,86 +967,84 @@ Lily_lexer::scan_escaped_word (string str)
                        cs = SCM_CAR (cs);
                }
 
                        cs = SCM_CAR (cs);
                }
 
-               if (scm_is_eq (cs, ly_lily_module_constant ("ly:music?")))
+               if (scm_is_eq (cs, SCM (Lily::ly_music_p)))
                        funtype = MUSIC_FUNCTION;
                        funtype = MUSIC_FUNCTION;
-               else if (scm_is_eq (cs, ly_lily_module_constant ("ly:event?")))
+               else if (scm_is_eq (cs, SCM (Lily::ly_event_p)))
                        funtype = EVENT_FUNCTION;
                else if (ly_is_procedure (cs))
                        funtype = SCM_FUNCTION;
                else programming_error ("Bad syntax function predicate");
 
                        funtype = EVENT_FUNCTION;
                else if (ly_is_procedure (cs))
                        funtype = SCM_FUNCTION;
                else programming_error ("Bad syntax function predicate");
 
-               push_extra_token (EXPECT_NO_MORE_ARGS);
+               push_extra_token (here_input (), EXPECT_NO_MORE_ARGS);
                for (s = scm_cdr (s); scm_is_pair (s); s = scm_cdr (s))
                {
                for (s = scm_cdr (s); scm_is_pair (s); s = scm_cdr (s))
                {
+                       SCM optional = SCM_UNDEFINED;
                        cs = scm_car (s);
                        cs = scm_car (s);
-                       
-                       if (cs == ly_music_p_proc)
-                               push_extra_token (EXPECT_MUSIC);
-                       else 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 (cs == ly_lily_module_constant ("markup?"))
-                               push_extra_token (EXPECT_MARKUP);
-                       else if (ly_is_procedure (cs))
-                               push_extra_token (EXPECT_SCM);
-                       else programming_error ("Function parameter without type-checking predicate");
+
+                       if (scm_is_pair (cs))
+                       {
+                               optional = SCM_CDR (cs);
+                               cs = SCM_CAR (cs);
+                       }
+
+                       if (ly_is_procedure (cs))
+                               push_extra_token (here_input (), EXPECT_SCM, cs);
+                       else
+                       {
+                               programming_error ("Function parameter without type-checking predicate");
+                               continue;
+                       }
+                       if (!scm_is_eq (optional, SCM_UNDEFINED))
+                               push_extra_token (here_input (), EXPECT_OPTIONAL, optional);
                }
                return funtype;
        }
                }
                return funtype;
        }
-
-       if (sid != SCM_UNDEFINED)
-       {
-               yylval.scm = sid;
-               return identifier_type (sid);
-       }
-
-       string msg (_f ("unknown escaped string: `\\%s'", str));        
-       LexerError (msg.c_str ());
-
-       yylval.scm = ly_string2scm (str);
-
-       return STRING;
+       yylval = sid;
+       return identifier_type (sid);
 }
 
 int
 }
 
 int
-Lily_lexer::scan_bare_word (string str)
+Lily_lexer::scan_word (SCM & output, SCM sym)
 {
 {
-       SCM sym = ly_symbol2scm (str.c_str ());
        if ((YYSTATE == notes) || (YYSTATE == chords)) {
                SCM handle = SCM_BOOL_F;
                if (scm_is_pair (pitchname_tab_stack_))
        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)) {
                if (scm_is_pair (handle)) {
-                       yylval.scm = scm_cdr (handle);
-                       if (unsmob_pitch (yylval.scm)) 
+                       output = scm_cdr (handle);
+                       if (unsmob<Pitch> (yylval))
                            return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
                            return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
-                       else if (scm_is_symbol (yylval.scm))
+                       else if (scm_is_symbol (yylval))
                            return DRUM_PITCH;
                }
                else if ((YYSTATE == chords)
                            return DRUM_PITCH;
                }
                else if ((YYSTATE == chords)
-                       && (handle = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
+                       && scm_is_true (handle = scm_hashq_get_handle (chordmodifier_tab_, sym)))
                {
                {
-                   yylval.scm = scm_cdr (handle);
+                   output = scm_cdr (handle);
                    return CHORD_MODIFIER;
                }
                    return CHORD_MODIFIER;
                }
-               if ((chord_repetition_.repetition_symbol_ != SCM_EOL)
-                   && to_boolean (scm_equal_p (chord_repetition_.repetition_symbol_, sym)))
-                       return CHORD_REPETITION;
        }
        }
+       output = SCM_UNDEFINED;
+       return -1;
+}
 
 
-       yylval.scm = ly_string2scm (str);
-       return STRING;
+int
+Lily_lexer::scan_bare_word (const string &str)
+{
+       int state = scan_word (yylval, ly_symbol2scm (str.c_str ()));
+       if (state >= 0)
+       {
+               return state;
+       }
+       yylval = ly_string2scm (str);
+       return SYMBOL;
 }
 
 int
 Lily_lexer::get_state () const
 {
 }
 
 int
 Lily_lexer::get_state () const
 {
-       if (YY_START == extratoken)
-               return hidden_state_;
-       else
-               return YY_START;
+       return YY_START;
 }
 
 bool
 }
 
 bool
@@ -921,9 +1071,188 @@ Lily_lexer::is_figure_state () const
        return get_state () == figures;
 }
 
        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, Input hi, char extra_token)
+{
+       SCM sval = SCM_UNDEFINED;
+
+       if (!SCM_UNBNDP (readerdata))
+       {
+               sval = ly_eval_scm (readerdata,
+                                   hi,
+                                   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 p = scm_reverse (scm_cdr (sval));
+                            scm_is_pair (p);
+                            p = scm_cdr (p))
+                       {
+                               SCM v = scm_car (p);
+                               if (Music *m = unsmob<Music> (v))
+                               {
+                                       if (!unsmob<Input> (m->get_property ("origin")))
+                                               m->set_spot (override_input (here_input ()));
+                               }
+
+                               int token;
+                               switch (extra_token) {
+                               case '$':
+                                       token = scan_scm_id (v);
+                                       if (!scm_is_eq (yylval, SCM_UNSPECIFIED))
+                                               push_extra_token (here_input (),
+                                                                 token, yylval);
+                                       break;
+                               case '#':
+                                       push_extra_token (here_input (),
+                                                         SCM_IDENTIFIER, v);
+                                       break;
+                               }
+                       }
+                       sval = scm_car (sval);
+               } else
+                       sval = SCM_UNSPECIFIED;
+       }
+
+       if (Music *m = unsmob<Music> (sval))
+       {
+               if (!unsmob<Input> (m->get_property ("origin")))
+                       m->set_spot (override_input (here_input ()));
+       }
+
+       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)
 /*
  urg, belong to string (_convert)
- and should be generalised 
+ and should be generalised
  */
 void
 strip_leading_white (string&s)
  */
 void
 strip_leading_white (string&s)
@@ -939,8 +1268,8 @@ strip_leading_white (string&s)
 void
 strip_trailing_white (string&s)
 {
 void
 strip_trailing_white (string&s)
 {
-       ssize i = s.length ();  
-       while (i--) 
+       ssize i = s.length ();
+       while (i--)
                if (!isspace (s[i]))
                        break;
 
                if (!isspace (s[i]))
                        break;
 
@@ -957,8 +1286,13 @@ is_valid_version (string s)
 {
   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
   Lilypond_version ver (s);
 {
   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
   Lilypond_version ver (s);
-  if (int (ver) < oldest_version)
-       {       
+  if (!ver)
+  {
+         non_fatal_error (_f ("Invalid version string \"%s\"", s));
+         return false;
+  }
+  if (ver < oldest_version)
+       {
                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;
                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;
@@ -971,33 +1305,21 @@ is_valid_version (string s)
        }
   return true;
 }
        }
   return true;
 }
-       
+
 
 /*
 
 /*
-  substitute _ and \,
+  substitute _
 */
 string
 lyric_fudge (string s)
 {
 */
 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;
 }
 
 /*
 }
 
 /*
@@ -1010,23 +1332,20 @@ scan_fraction (string frac)
        string left = frac.substr (0, i);
        string right = frac.substr (i + 1, (frac.length () - i + 1));
 
        string left = frac.substr (0, i);
        string right = frac.substr (i + 1, (frac.length () - i + 1));
 
-       int n = String_convert::dec2int (left);
-       int d = String_convert::dec2int (right);
-       return scm_cons (scm_from_int (n), scm_from_int (d));
+       return scm_cons (scm_c_read_string (left.c_str ()),
+                        scm_c_read_string (right.c_str ()));
 }
 
 SCM
 lookup_markup_command (string s)
 {
 }
 
 SCM
 lookup_markup_command (string s)
 {
-       SCM proc = ly_lily_module_constant ("lookup-markup-command");
-       return scm_call_1 (proc, ly_string2scm (s));
+       return Lily::lookup_markup_command (ly_string2scm (s));
 }
 
 SCM
 lookup_markup_list_command (string 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));
+       return Lily::lookup_markup_list_command (ly_string2scm (s));
 }
 
 /* Shut up lexer warnings.  */
 }
 
 /* Shut up lexer warnings.  */