[svn] r7811: nemerle/trunk: macros/io.n
ncc/testsuite/negative/tyenf.n
VladD2
svnadmin at nemerle.org
Wed Oct 17 16:12:04 CEST 2007
Log:
Add NemerleStringTemplate functionality.
Author: VladD2
Date: Wed Oct 17 16:12:01 2007
New Revision: 7811
Modified:
nemerle/trunk/macros/io.n
nemerle/trunk/ncc/testsuite/negative/tyenf.n
Modified: nemerle/trunk/macros/io.n
==============================================================================
--- nemerle/trunk/macros/io.n (original)
+++ nemerle/trunk/macros/io.n Wed Oct 17 16:12:01 2007
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2003, 2004 The University of Wroclaw.
* All rights reserved.
*
@@ -26,15 +26,25 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+using StringTemplate;
+
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Collections;
using Nemerle.Utility;
+using Nemerle.Compiler.Parsetree;
using System;
+using System.Diagnostics.Trace;
+using System.Reflection;
using System.Text;
+using TT = Nemerle.Compiler.Typedtree;
+using TExpr = Nemerle.Compiler.Typedtree.TExpr;
using PT = Nemerle.Compiler.Parsetree;
+using BF = System.Reflection.BindingFlags;
+using SCG = System.Collections.Generic;
+using SB = Ext;//Nemerle.Utility.NStringBuilderExtensions;
namespace Nemerle.IO
{
@@ -112,26 +122,59 @@
]>
}
- /** If string literal is supplied, then prints it to System.Console, replacing all
- occurences of $id with id.ToString () invocation
- If any other expression is supplied, it is equivalent to System.Console.Write
- */
- macro print (value)
+ macro sprint(str : string)
{
- match (value) {
- | <[ $(str : string) ]> =>
- def seq = List.RevMap (make_splice_distribution (str, Macros.ImplicitCTX().Env), fun (x) {
- <[ Console.Write ($x) ]>
- });
- <[ {.. $seq } ]>
+ if (string.IsNullOrEmpty (str))
+ {
+ Message.Warning ("empty spliced string");
+ <[ string.Empty ]>
+ }
+ else
+ {
+ def seq = StringTemplate.Helper.make_splice_distribution (str, Macros.ImplicitCTX().Env).Rev ();
+
+ match (seq)
+ {
+ | [StrPart.Lit(val)] =>
+ Message.Warning ($"spliced string without splices: '$str'");
+ <[ $(val : string) ]> //PT.PExpr.Literal(Literal.String(val))
| _ =>
- <[ Console.Write ($value) ]>
+ //def indentPresent = seq.Exists(_ is StrPart.IndentedExpr);
+ //def seq = if (indentPresent) StrPart.Expr(<[ def ident = ""; ]>) :: seq;
+ //mutable curIndent = "";
+ def seq = seq;
+
+ def res = seq.Map(e =>
+ match (e : StrPart)
+ {
+ | Lit(str) => <[ $(str : string) ]>
+ // TODO: Try add support of identation.
+ | NewLine => <[ Environment.NewLine ]>
+ | Expr(expr) => expr
+ | IndentedExpr(indent, expr) =>
+ <[ def indent = $(indent : string);
+ indent + Convert.ToString($expr) ]>
+ });
+
+ <[ string.Concat (..$res) ]>
+ }
}
}
- macro sprint (str : string)
+ macro fprint (writer, str : string)
{
+ <[ $writer.Write(sprint($(str : string))) ]>
+ }
+
+ /// If string literal is supplied, then prints it to System.Console, replacing all
+ /// occurences of $id with id.ToString () invocation
+ /// If any other expression is supplied, it is equivalent to System.Console.Write
+ macro print (value)
+ {
+ match (value)
+ {
+ | <[ $(str : string) ]> =>
if (string.IsNullOrEmpty (str))
{
Message.Warning ("empty spliced string");
@@ -139,26 +182,38 @@
}
else
{
- def seq = make_splice_distribution (str, Macros.ImplicitCTX().Env).Rev ();
+ def seq = StringTemplate.Helper.make_splice_distribution (str, Macros.ImplicitCTX().Env).Rev ();
match (seq)
{
- | [PT.PExpr.Literal as lit] =>
- Message.Warning ($"spliced string without splices: '$str'");
- lit
+ | [StrPart.Lit(val)] =>
+ <[ Console.Write ($(val : string)) ]>
- | _ => <[ string.Concat (..$seq) ]>
+ | _ =>
+ //def indentPresent = seq.Exists(_ is StrPart.IndentedExpr);
+ //def seq = if (indentPresent) StrPart.Expr(<[ def ident = ""; ]>) :: seq;
+ //mutable curIndent = "";
+ def seq = seq;
+
+ def res = seq.Map(e =>
+ match (e : StrPart)
+ {
+ | Lit(str) => <[ $(str : string) ]>
+ // TODO: Try add support of identation.
+ | NewLine => <[ Environment.NewLine ]>
+ | Expr(expr) => expr
+ | IndentedExpr(indent, expr) =>
+ <[ def indent = $(indent : string);
+ indent + Convert.ToString($expr) ]>
+ });
+
+ <[ Console.Write (string.Concat (..$res)) ]>
}
}
+
+ | _ => <[ Console.Write ($value) ]>
}
- /** Writes text to given System.IO.TextWriter */
- macro fprint (writer, str : string)
- {
- def seq = List.RevMap (make_splice_distribution (str, Macros.ImplicitCTX().Env), fun (x) {
- <[ writer_v.Write ($x) ]>
- });
- <[ def writer_v = $writer : IO.TextWriter; {.. $seq }; ]>
}
// module internal to this assembly used for compile time analysis of string formatters, etc.
@@ -345,12 +400,378 @@
iter_through (parse_format (format), parms.Length, []);
}
- /** for $(...) and ..$(...) expressions:
- - first evaluate expressions
- - store intermediate results in variables
- - return list of evaluators and reference variables in reverse order
- */
- public make_splice_distribution (str : string, env : GlobalEnv) : list [PT.PExpr]
+/*
+
+ /// If string literal is supplied, then prints it to System.Console, replacing all
+ /// occurences of $id with id.ToString () invocation
+ /// If any other expression is supplied, it is equivalent to System.Console.Write
+ macro print (value)
+ {
+ match (value) {
+ | <[ $(str : string) ]> =>
+ def seq = List.RevMap (make_splice_distribution (str, Macros.ImplicitCTX().Env), fun (x) {
+ <[ Console.Write ($x) ]>
+ });
+ <[ {.. $seq } ]>
+
+ | _ =>
+ <[ Console.Write ($value) ]>
+ }
+ }
+
+ macro sprint (str : string)
+ {
+ if (string.IsNullOrEmpty (str))
+ {
+ Message.Warning ("empty spliced string");
+ <[ string.Empty ]>
+ }
+ else
+ {
+ def seq = make_splice_distribution (str, Macros.ImplicitCTX().Env).Rev ();
+
+ match (seq)
+ {
+ | [PT.PExpr.Literal as lit] =>
+ Message.Warning ($"spliced string without splices: '$str'");
+ lit
+
+ | _ => <[ string.Concat (..$seq) ]>
+ }
+ }
+ }
+
+ /// Writes text to given System.IO.TextWriter
+ macro fprint (writer, str : string)
+ {
+ def seq = List.RevMap (make_splice_distribution (str, Macros.ImplicitCTX().Env), fun (x) {
+ <[ writer_v.Write ($x) ]>
+ });
+ <[ def writer_v = $writer : IO.TextWriter; {.. $seq }; ]>
+ }
+*/
+ }
+}
+
+namespace StringTemplate
+{
+ using StringTemplate.Helper;
+
+ [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Class, Inherited = true)]
+ macro StringTemplateGroup(tb : TypeBuilder)
+ {
+ Helper2.BeforeTypedMembers(tb, Nemerle.Macros.ImplicitCTX());
+ }
+
+ [MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = true)]
+ macro StringTemplateGroup(tb : TypeBuilder)
+ {
+ Helper2.WithTypedMembers(tb, Nemerle.Macros.ImplicitCTX());
+ }
+
+ internal module Helper2
+ {
+ StSuffix = "__StImpl";
+
+ internal BeforeTypedMembers(tb : TypeBuilder, ctx : Typer) : void
+ {
+ ctx.Env.Manager.MacroColors.PushUseSiteColor();
+ try
+ {
+ // Add <MethodName>__StImpl method into STG.
+
+ def members1 = tb.GetParsedMembers(true);
+ def ideTest = m => m.Tokens != null // BUG in compiler! Workaround!
+ && m.Tokens is Token.BracesGroup(Token.LooseGroup(Token.StringLiteral(_)));
+ def compilerTest = m => m.Body is PExpr.Sequence([PExpr.Literal(Literal.String(_))]);
+ def test = if (ctx.Manager.IsIntelliSenseMode) ideTest else compilerTest;
+ def members2 = members1.Filter(_ => { | m is ClassMember.Function when test(m) => true | _ => false});
+
+ foreach (method is ClassMember.Function in members2)
+ {
+ def h = method.header;
+ def workMethName = method.Name + StSuffix;
+ def newMethodAst = <[ decl:
+ protected virtual $(workMethName : usesite) [..$(h.typarms.tyvars)] (..$(h.Parameters)) : void
+ where ..$(h.typarms.constraints)
+ {
+ }]>;
+ _ = tb.DefineWithSource(newMethodAst);
+ method.UserData = newMethodAst;
+ method.Attributes |= NemerleAttributes.Static | NemerleAttributes.Public;
+ }
+ }
+ finally { ctx.Env.Manager.MacroColors.PopColor(); }
+ }
+
+ internal WithTypedMembers(tb : TypeBuilder, ctx : Typer) : void
+ {
+ ctx.Env.Manager.MacroColors.PushUseSiteColor();
+ try
+ {
+ tb.Define(<[ decl: protected _builder : StringBuilder = StringBuilder(1024); ]>);
+ tb.Define(<[ decl: protected mutable _indent : string = "\n"; ]>);
+ tb.Define(<[ decl: protected _indentLenStack : Stack[int] = Stack(); ]>);
+ _ = tb.DefineWithSource(<[ decl:
+ protected AddIndent(indent : string) : void
+ {
+ _indentLenStack.Add(indent.Length);
+ _indent += indent;
+ }
+ ]>);
+ _ = tb.DefineWithSource(<[ decl:
+ protected RemoveLastIndent() : void
+ {
+ _indent = _indent.Substring(0, _indent.Length - _indentLenStack.Pop());
+ }
+ ]>);
+ _ = tb.DefineWithSource(<[ decl:
+ protected PrintNewLineAndIndent() : void
+ {
+ _ = _builder.Append(_indent);
+ }
+ ]>);
+
+ def beforeBodyTypingHandler(mb : MethodBuilder, pBody) : PExpr
+ {
+ def pBody = pBody;
+
+ match (pBody)
+ {
+ | PExpr.Sequence([PExpr.Literal(Literal.String(str)) as lit]) =>
+
+ // 1. Сгенерировать и подставить тело для х__StImpl-метода.
+ // 2. В тело данного метода подставить код вызова этого метода.
+
+ def findCorrespondMethod()
+ {
+ def stAst = mb.Ast.UserData;
+ def stMethods = tb.GetMethods(BF.DeclaredOnly | BF.Instance | BF.NonPublic).FindAll(
+ _ => { | m is MethodBuilder when m.Ast == stAst => true | _ => false });
+
+ match (stMethods)
+ {
+ | [m is MethodBuilder] => m
+ | _ => Util.ice($"Found to many correspond 'string template' methods: ..$stMethods");
+ }
+ }
+
+ def expr = MakeStringTemplateExpr(mb, str, lit.Location, ctx);
+ def m = findCorrespondMethod();
+
+ m.Body = expr;
+
+ def h = mb.Ast.header;
+ def workMethName = mb.Ast.Name + StSuffix;
+
+ <[ def instance = $(tb.FullName : usesite)();
+ instance.$(workMethName : usesite)(..$(h.ParametersReferences));
+ instance._builder.ToString() ]>
+
+ | _ => pBody
+ }
+ }
+
+ foreach (mb is MethodBuilder in tb.GetMethods(BF.DeclaredOnly | BF.Static | BF.Public))
+ when (mb.Ast.UserData is ClassMember.Function)
+ mb.AddBeforeBodyTypingHandler(beforeBodyTypingHandler);
+ }
+ finally { ctx.Env.Manager.MacroColors.PopColor(); }
+ }
+
+ MakeStringTemplateExpr(mb : MethodBuilder, template : string, loc : Location, ctx : Typer) : PExpr
+ {
+ def (template, loc) = SquareString(template, loc);
+ def makeEllipsisSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
+ {
+ if (isComplexExpr)
+ {
+ def pExpr = MainParser.ParseExpr (env, expr, loc);
+ match (pExpr)
+ {
+ | <[ $seqExpr; $sep; $cnvFuncExpr; ]> =>
+
+ def loc = loc;
+ _ = loc;
+
+ // If cnvFuncExpr is a StrinTemplete reference,
+ // replace it by <methodName>__StImpl-method.
+
+ def mb = mb;
+ // Find __StImpl-method corresponding to mb.
+ def corespStImplMethod = (mb.Ast.UserData :> ClassMember.Function).Builder;
+ // Type fake expression to determinate what is cnvFuncExpr (it type).
+ def expr = <[ NCollectionsUtils.MapLazy($seqExpr, $cnvFuncExpr) ]>;
+ def typer = Typer(corespStImplMethod);
+
+ _ = typer.TypeExpr(expr);
+
+ match (cnvFuncExpr.TypedObject)
+ {
+ | TExpr.StaticRef(_, m is MethodBuilder, _)
+ when m.DeclaringType.Equals(mb.DeclaringType) =>
+ match (m.Ast.UserData)
+ {
+ | coresp is ClassMember.Function =>
+ <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, this.$(coresp.Name : usesite)); ]>
+ | _ => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, $cnvFuncExpr); ]>
+ }
+ | _ => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, $cnvFuncExpr); ]>
+ }
+
+ | <[ $seqExpr; $sep; ]> => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent); ]>
+ | _ => <[ SB.AppendSeq(_builder, $pExpr, ", ", _indent); ]>
+ }
+ }
+ else if (expr == "this") <[ SB.AppendSeq(_builder, this, ", ", _indent); ]>
+ else <[ SB.AppendSeq(_builder, $(expr : usesite), ", ", _indent); ]>
+ }
+ def makeSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
+ {
+ def makeExpr(pExpr)
+ {
+ <[
+ if (_indent.Length > 1)
+ {
+ def pos = _builder.Length;
+ _ = _builder.Append($pExpr);
+ _ = _builder.Replace("\n", _indent, pos, _builder.Length - pos);
+ }
+ else
+ _ = _builder.Append($pExpr);
+ ]>
+ }
+ if (isComplexExpr)
+ {
+ env.Manager.MacroColors.PushUseSiteColor ();
+ try
+ {
+ def _builder = StringBuilder();
+ def pExpr = MainParser.ParseExpr(env, expr);
+ makeExpr(pExpr);
+ }
+ finally { env.Manager.MacroColors.PopColor (); }
+ } else if (expr == "this") makeExpr(<[ this ]>);
+ else makeExpr(<[ $(expr : usesite) ]>);
+ }
+
+ def exprs = Helper.make_splice_distribution2(template, ctx.Env, makeSplaceExpr, makeEllipsisSplaceExpr);
+ //def isNeedOptimize(_ : list[StrPart])
+ //{
+ //| Lit(_) :: Lit(_) :: _ => true
+ //| _ :: tail => isNeedOptimize(tail)
+ //| [] => false
+ //}
+ //def optimize(_ : list[StrPart])
+ //{
+ //| Lit(s1) :: Lit(s2) :: tail => optimize(StrPart.Lit(s2 + s1) :: tail)
+ //| x :: tail => x :: optimize(tail)
+ //| [] => []
+ //}
+ //def exprs = if (isNeedOptimize(exprs)) optimize(exprs) else exprs;
+ def res = exprs.RevMap(e =>
+ match (e : StrPart)
+ {
+ | Lit(str) => <[ _ = _builder.Append($(str : string)); ]>
+ | NewLine => <[ _ = PrintNewLineAndIndent(); ]>
+ | Expr(expr) => expr
+ | IndentedExpr(indent, expr) => <[
+ def indent = $(indent : string);
+ _ = _builder.Append(indent);
+ AddIndent(indent);
+ $expr;
+ RemoveLastIndent(); ]>
+ });
+
+ <[ { ..$res } ]>
+ }
+
+ public static Last[T](this lst : SCG.IList[T]) : T
+ {
+ lst[lst.Count - 1]
+ }
+
+ /// If we have string like this
+ /// <#
+ /// SomeText1
+ /// SomeText2
+ /// #>
+ /// this function convert it to
+ /// "SomeText1\nSomeText2"
+ SquareString(str : string, loc : Location) : string * Location
+ {
+ match (str.LastIndexOfAny(array['\r', '\n']))
+ {
+ | -1 => (str, Location(loc.FileIndex, loc.Line, loc.Column,
+ loc.EndLine, loc.EndColumn))
+ | _ =>
+ def rows = str.Split(array["\r\n", "\n", "\r"], StringSplitOptions.None);
+ when (rows.Length <= 2)
+ Message.Error(loc, "The multiline String Template should cantain 3 and more row. "
+ "(First and last line in multiline String Template ignored.)");
+ def prefix = rows.Last();
+ def firstIndex = if (rows[0].ForAll(char.IsWhiteSpace)) 1 else 0;
+ def sb = StringBuilder(str.Length - prefix.Length
+ - if (firstIndex == 1) rows[0].Length else 0);
+ def len = rows.Length - 1;
+ mutable isIndentMismatch = false;
+ for (mutable i = firstIndex; i < len; i++)
+ {
+ def row = rows[i];
+ if (row.StartsWith(prefix, StringComparison.InvariantCulture))
+ _ = sb.AppendLine(row.Substring(prefix.Length, row.Length - prefix.Length));
+ else
+ {
+ Message.Error(Location(loc, loc.Line + i, 1, loc.Line + i, row.Length + 1),
+ "Mismatch of the string template strBuilder characters.");
+ isIndentMismatch = true;
+ _ = sb.AppendLine(row);
+ }
+ }
+
+ when (sb.Length > Environment.NewLine.Length)
+ sb.Length -= Environment.NewLine.Length;
+
+ when (isIndentMismatch)
+ Message.Hint(Location(loc, loc.EndLine, 1, loc.EndLine, loc.EndColumn),
+ "Please, make sure that all of the strBuilder characters of your "
+ "string template match the last line indentation.");
+ // TODO: Локейшон вычисляется неверно. Переделать.
+ (sb.ToString(), Location(loc.FileIndex,
+ loc.Line + firstIndex, prefix.Length + 1,
+ loc.EndLine - firstIndex, rows[len - 1].Length + 1)); // + 1 => Location coordinates 1 bound
+ }
+ }
+
+ //public static Offset(this loc : Location, lineOffset : int, colOffset : int) : Location
+ //{
+ //Location(loc, loc.Line + lineOffset, loc.Column + colOffset,
+ //loc.EndLine + lineOffset, loc.EndColumn + colOffset)
+ //}
+ }
+
+ variant StrPart
+ {
+ | Lit { str : string; }
+ | Expr { expr : PT.PExpr; }
+ | NewLine
+ | IndentedExpr { indent : string; expr : PT.PExpr; }
+
+ public override ToString() : string
+ {
+ match (this)
+ {
+ | Lit(str) => $"Lit: '$str'"
+ | Expr(expr) => $"Expr: $expr"
+ | NewLine => "<\n>"
+ | IndentedExpr(indent, expr) => $"IndentedExpr: '$expr' ('$indent')"
+ }
+ }
+ }
+
+ internal module Helper
+ {
+ public make_splice_distribution (str : string, env : GlobalEnv) : list [StrPart]
{
def makeEllipsisSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
{
@@ -392,135 +813,342 @@
else <[ Convert.ToString ($(expr : usesite)) ]>;
}
- make_splice_distribution (str, env, makeSplaceExpr, makeEllipsisSplaceExpr)
+ make_splice_distribution2 (str, env, makeSplaceExpr, makeEllipsisSplaceExpr)
}
- public make_splice_distribution (
+ /** for $(...) and ..$(...) expressions:
+ - first evaluate expressions
+ - store intermediate results in variables
+ - return list of evaluators and reference variables in reverse order
+ */
+ public make_splice_distribution2 (
str : string,
- _env : GlobalEnv,
+ env : GlobalEnv,
makeSplaceExpr : GlobalEnv * string * bool -> PT.PExpr, // env * strExpr * isComplexExpr
makeEllipsisSplaceExpr : GlobalEnv * string * bool -> PT.PExpr // env * strExpr * isComplexExpr
)
- : list [PT.PExpr]
+ : list [StrPart]
+ {
+ mutable nestLevel = 0;
+ mutable index = -1;
+ mutable ch = if (str.Length > 0) str[0] else '\0';
+ def strBuilder = StringBuilder();
+ def peekN(n) { def next = index + n; if (next < str.Length) str[next] else '\0' }
+ def peek() { peekN(1) }
+ def next() { ch = peek(); index++; ch }
+ def getStrFromBuilder() { def res = strBuilder.ToString(); strBuilder.Length = 0; res }
+ def appendToBuilder(chr) { _ = strBuilder.Append(chr) }
+ /// ~~~Parse expression based on nested brackets.
+ /// Разбирает строку производя поиск закрывающей скобки.
+ /// Вложенные скобки игнорируются. В итоге получается строка содержащая
+ /// выражение заключенное в скбоки (которое так же может содержать вложенные скобки)
+ /// и булево значение говорящее, содержится ли в строке простой идентификатор или варажение.
+ /// Returns pare of (exprStr * isIdentifier)
+ def parseExpressionStr() : string * bool
+ {
+ Assert(strBuilder.Length == 0, "strBuilder.Length == 0");
+ Assert(ch == '(', "ch == '('");
+ /// exprStr * allIsAlphNum
+ def loop(balance, allIsAlphNum) : string * bool
+ {
+ match (peek())
+ {
+ // TODO: Обработать ситуацию когда скобка не закрыта! См. файл:
+ // C:\MyProjects\Nemerle\nemerle\ncc\testsuite\negative\tyenf.n
+ | '\0'
+ | ')' when balance == 1 => _ = next(); (getStrFromBuilder(), allIsAlphNum)
+ | ')' => appendToBuilder(next()); loop(balance - 1, false)
+ | '(' => appendToBuilder(next()); loop(balance + 1, false)
+ | curCh =>
+ appendToBuilder(next());
+ loop(balance, allIsAlphNum && (char.IsLetterOrDigit(curCh) || curCh == '_'))
+ }
+ }
+
+ def (expr, allIsAlphNum) = loop(1, true);
+ (expr, allIsAlphNum && expr.Length != 0 && expr != "_" && char.IsLetter(expr[0]))
+ }
+ def parseIdentifier()
{
- mutable seen_non_alnum = false;
+ Assert(strBuilder.Length == 0, "strBuilder.Length == 0");
- def find_end (balance, idx) {
- when (idx >= str.Length)
- Message.FatalError ("runaway $(...) in format string");
+ def loop()
+ {
+ def curCh = peek();
+ match (curCh)
+ {
+ | '_'
+ | _ when char.IsLetterOrDigit(curCh) => appendToBuilder(next()); loop()
+ | _ => getStrFromBuilder()
+ }
+ }
- def ch = str[idx];
- seen_non_alnum = seen_non_alnum || !(System.Char.IsLetterOrDigit (ch) || ch == '_');
- match (ch) {
- | ')' when balance == 1 => idx
- | ')' => find_end (balance - 1, idx + 1)
- | '(' => find_end (balance + 1, idx + 1)
- | _ => find_end (balance, idx + 1)
+ if (ch == '_' || char.IsLetter(ch))
+ {
+ appendToBuilder(ch);
+ loop()
}
+ else ""
}
- def find_end_normal (idx) {
- if (idx >= str.Length) idx
+ def loop (res : list[StrPart]) : list[StrPart]
+ {
+ nestLevel++; Diagnostics.Trace.Assert(nestLevel < 20000, "Prevent stack owerflow"); // Prevent stack owerflow
+
+ // Завершает акомуляцию сиволов литерала и создает соотвествующую
+ // лексему добавляя ее к началу списка лексем
+ def endLiteral()
+ {
+ if (strBuilder.Length == 0)
+ res
else
- match (str[idx]) {
- | '_'
- | ch when System.Char.IsLetterOrDigit (ch) => find_end_normal (idx + 1)
- | _ => idx
+ StrPart.Lit(getStrFromBuilder()) :: res
}
+ def isNextDollar(n)
+ {
+ def ch1 = peekN(n);
+ if (char.IsWhiteSpace(ch1)) isNextDollar(n + 1)
+ else ch1 == '$'
}
+ def isElipse() { peek() == '.' && isNextDollar(2) }
+ def processNewLine() { loop (StrPart.NewLine() :: endLiteral()) }
- def indexOfDollar(idx)
+ match (next())
+ {
+ | '\0' => endLiteral()
+ | '$' => parceSpliceEx(endLiteral(), true)
+ | '.' when isElipse() => index = str.IndexOf('$', index);
+ parceSpliceEx(endLiteral(), false); // '..$'
+ | '\r' when peek() == '\n' => _ = next(); processNewLine()
+ | '\n' | '\r' => processNewLine()
+ | x => appendToBuilder(x); loop(res)
+ }
+ }
+ and parceSpliceEx(res, isSimple)
+ {
+ when (next() == '\0')
{
- def str = str;
- if (idx >= str.Length) -1
- else if (char.IsWhiteSpace(str[idx])) indexOfDollar(idx + 1)
- else if (str[idx] == '$') idx
- else -1
+ //Diagnostics.Trace.Assert(false);
+ Message.Error ("lone `$' at the end of the format string");
+ Nemerle.Imperative.Return ([StrPart.Lit("$")]);
}
- mutable nestLevel = 0;
+ def rtyIndent(res : list[StrPart], expr)
+ {
+ match (res)
+ {
+ | Lit(str) :: NewLine :: tail when str.ForAll(char.IsWhiteSpace) =>
+ StrPart.IndentedExpr(str, expr) :: StrPart.NewLine() :: tail
+ | _ => StrPart.Expr(expr) :: res
+ }
+ }
+
+ def str = str; _ = str;
- def loop (res, idx)
+ if (ch == '(')
{
- def str = str;
- nestLevel++;
- Diagnostics.Trace.Assert(nestLevel < 200);
+ //def index1 = index; _ = index1;
+ def (exprStr, isIdentifier) = parseExpressionStr();
- if (idx < 0 || idx >= str.Length)
- res
- else if (str[idx] == '.') // try recognize pattern '..$x'
+ if (ch == '\0') // скобка не закрыта
{
- if (idx + 3 < str.Length && str[idx + 1] == '.' && indexOfDollar(idx + 2) >= 0)
- parceSpliceEx(res, indexOfDollar(idx + 2), false); // found pattern '..$'
- else
- loop (<[$("." : string)]> :: res, idx + 1)
+ def exprStr = "(" + exprStr;
+ Message.Error($"no closing bracket found in `$(exprStr)' "
+ "(the closing bracket in format string is probably missing)");
}
- else if (str[idx] == '$')
- parceSpliceEx(res, idx, true)
+ else when (exprStr.Trim().Length == 0)
+ Message.Error("expression without content");
+
+ def expr = if (isSimple) makeSplaceExpr(env, exprStr, !isIdentifier)
+ else makeEllipsisSplaceExpr(env, exprStr, !isIdentifier);
+ loop (rtyIndent(res, expr))
+ }
+ else if (ch == '$')
+ loop (StrPart.Lit("$") :: res)
else
{
- def str = str;
- def get(index) { if (index < 0 || index >= str.Length) '\0' else str[index] }
- def getElipseIndex(index)
- { // If '..' prefix exists return it index. Otherwise return initial index.
- def ch = get(index - 1);
+ //def index1 = index; _ = index1;
+ def variableName = parseIdentifier();
- if (char.IsWhiteSpace(ch)) getElipseIndex(index - 1)
- else if (get(index - 1) == '.' && get(index - 2) == '.') index - 2
- else -1
+ if (variableName == "")
+ {
+ appendToBuilder(ch);
+ Message.Warning ("expected variable name or expression enclosed with (..) after $ in splice string");
+ loop (StrPart.Lit("$") :: res)
}
- def nextIdx = str.IndexOf('$', idx);
- def elipseIdx = getElipseIndex(nextIdx);
- def nextIdx = if (elipseIdx >= 0) elipseIdx else nextIdx;
- def next_str = if (nextIdx == -1) str.Substring (idx)
- else str.Substring (idx, nextIdx - idx);
- loop (<[ $(next_str : string) ]> :: res, nextIdx)
+ else
+ {
+ def expr = if (isSimple) makeSplaceExpr(env, variableName, false)
+ else makeEllipsisSplaceExpr(env, variableName, false);
+ //def index1 = index; _ = index1;
+ loop (rtyIndent(res, expr))
}
}
- and parceSpliceEx(res, idx, isSimple)
+ }
+
+ loop ([])
+ }
+ }
+}
+
+
+public module Ext
+{
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ indent : string,
+ convert : T -> string
+ )
+ : void
{
- def str = str;
+ mutable firstTime = true;
- when (idx + 1 >= str.Length)
+ Assert(true);
+
+ foreach (elem in seq)
{
- //Diagnostics.Trace.Assert(false);
- Message.Error ("lone `$' at the end of the format string");
- Nemerle.Imperative.Return ([<[ "$" ]>]);
+ if (firstTime)
+ firstTime = false;
+ else
+ {
+ def pos = builder.Length;
+ _ = builder.Append(seperator);
+ _ = builder.Replace("\n", indent, pos, seperator.Length);
+ }
+
+ def str = convert(elem).Replace("\n", indent);
+ _ = builder.Append(str);
+ }
}
- def nextIndex = idx + 1;
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ indent : string
+ )
+ : void
+ {
+ mutable firstTime = true;
+
+ Assert(true);
- if (str[nextIndex] == '(')
+ foreach (elem in seq)
{
- def end = find_end (1, idx + 2);
- def expr = str.Substring (idx + 2, end - idx - 2);
- def isComplexExpr = expr == "" || expr == "_" || seen_non_alnum || char.IsDigit(expr[0]);
- def expr = if (isSimple) makeSplaceExpr(_env, expr, isComplexExpr)
- else makeEllipsisSplaceExpr(_env, expr, isComplexExpr);
- loop (expr :: res, end + 1)
+ if (firstTime)
+ firstTime = false;
+ else
+ {
+ def pos = builder.Length;
+ _ = builder.Append(seperator);
+ _ = builder.Replace("\n", indent, pos, seperator.Length);
}
- else if (str[nextIndex] == '$')
- loop (<[$("$" : string)]> :: res, idx + 2)
+
+ def str = elem.ToString().Replace("\n", indent);
+ _ = builder.Append(str);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (this builder : NStringBuilder, seq : SCG.IEnumerable [T], seperator : string) : void
+ {
+ Assert(false);
+ mutable firstTime = true;
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
else
+ _ = builder.Append(seperator);
+
+ _ = builder.Append(elem);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ convert : T -> string
+ )
+ : void
{
- def end = find_end_normal (nextIndex);
- def variable_name = str.Substring (nextIndex, end - idx - 1);
+ Assert(false);
+ mutable firstTime = true;
- if (variable_name == "")
+ foreach (elem in seq)
{
- Message.Warning ("expected variable name or expression enclosed with (..) after $ in splice string");
- loop (<[$("$" : string)]> :: res, nextIndex)
+ if (firstTime)
+ firstTime = false;
+ else
+ _ = builder.Append(seperator);
+
+ def str = convert(elem);
+ _ = builder.Append(str);
}
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ indent : string,
+ convert : T -> void
+ )
+ : void
+ {
+ Assert(true);
+ mutable firstTime = true;
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
else
{
- def expr = if (isSimple) makeSplaceExpr(_env, variable_name, false)
- else makeEllipsisSplaceExpr(_env, variable_name, false);
- loop (expr :: res, end)
+ def pos = builder.Length;
+ _ = builder.Append(seperator);
+ _ = builder.Replace("\n", indent, pos, seperator.Length);
}
+
+ convert(elem);
}
}
- loop ([], 0)
- }
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (this builder : NStringBuilder, seq : list [T], seperator : string) : void
+ {
+ _ = builder.AppendList(seq, seperator);
}
}
Modified: nemerle/trunk/ncc/testsuite/negative/tyenf.n
==============================================================================
--- nemerle/trunk/ncc/testsuite/negative/tyenf.n (original)
+++ nemerle/trunk/ncc/testsuite/negative/tyenf.n Wed Oct 17 16:12:01 2007
@@ -27,7 +27,7 @@
_ = if (true) Fruit.Apple else null; // E: the `null' literal is not a valid value of type Fruit
_ = if (true) null else Fruit.Apple; // E: the `null' literal is not a valid value of type Fruit
- _ = $ "$(foo"; // E: runaway .* in format string
+ _ = $ "$(foo"; // E: no closing bracket found in `\(foo' \(the closing bracket in format string is probably missing\)
_ = $ "$(foo +)"; // E: parse error near operator
_ = $ "$()"; // E: expression without content
_ = $ ""; // W: empty spliced string
More information about the svn
mailing list