diff --git a/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs b/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs index f88b3534..1b564112 100644 --- a/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs +++ b/src/samples/IndentedWhile/parser/IndentedWhileParserGeneric.cs @@ -143,6 +143,27 @@ public WhileAST skipStmt(WhileAST expression) #region OPERANDS + // fstrings + [Production("primary : OPEN_FSTRING[d] fstring_element* CLOSE_FSTRING[d]")] + public WhileAST fstring(List elements) + { + var fstring = new FString(elements.Cast().ToList(), elements.First().Position); + return fstring; + } + + [Production("fstring_element : FSTRING_CONTENT")] + public WhileAST FStringContent(Token element) + { + return new FStringElement(new StringConstant(element.Value),element.Position); + } + + [Production("fstring_element : OPEN_FSTRING_EXPPRESSION[d] IDENTIFIER CLOSE_FSTRING_EXPPRESSION[d]")] + public WhileAST FStringExpression(Token element) + { + return new FStringElement(new Variable(element.Value),element.Position); + } + + [Production("primary: INT")] public WhileAST PrimaryInt(Token intToken) { @@ -156,11 +177,11 @@ public WhileAST PrimaryBool(Token boolToken) return new BoolConstant(bool.Parse(boolToken.StringWithoutQuotes)); } - [Production("primary: STRING")] - public WhileAST PrimaryString(Token stringToken) - { - return new StringConstant(stringToken.StringWithoutQuotes); - } + // [Production("primary: STRING")] + // public WhileAST PrimaryString(Token stringToken) + // { + // return new StringConstant(stringToken.StringWithoutQuotes); + // } [Production("primary: IDENTIFIER")] public WhileAST PrimaryId(Token varToken) diff --git a/src/samples/IndentedWhile/parser/IndentedWhileTokenGeneric.cs b/src/samples/IndentedWhile/parser/IndentedWhileTokenGeneric.cs index 39fa65cc..18ca272c 100644 --- a/src/samples/IndentedWhile/parser/IndentedWhileTokenGeneric.cs +++ b/src/samples/IndentedWhile/parser/IndentedWhileTokenGeneric.cs @@ -42,7 +42,7 @@ public enum IndentedWhileTokenGeneric [Lexeme(GenericToken.KeyWord, "PRINT")] [Lexeme(GenericToken.KeyWord, "print")] PRINT = 12, - + [Lexeme(GenericToken.KeyWord, "RETURN")] [Lexeme(GenericToken.KeyWord, "return")] RETURN = 13, @@ -50,9 +50,9 @@ public enum IndentedWhileTokenGeneric #region literals 20 -> 29 - [Lexeme(GenericToken.Identifier,IdentifierType.AlphaNumericDash)] IDENTIFIER = 20, - - [Lexeme(GenericToken.String)] STRING = 21, + [Mode(ModeAttribute.DefaultLexerMode, "fstringExpression")] + [Lexeme(GenericToken.Identifier, IdentifierType.AlphaNumericDash)] + IDENTIFIER = 20, [Lexeme(GenericToken.Int)] INT = 22, @@ -88,16 +88,40 @@ public enum IndentedWhileTokenGeneric #region sugar 50 -> - // [Lexeme(GenericToken.SugarToken, "(")] LPAREN = 50, - // - // [Lexeme(GenericToken.SugarToken, ")")] RPAREN = 51, [Lexeme(GenericToken.SugarToken, ";")] SEMICOLON = 52, - [SingleLineComment("#")] - COMMENT=1236 + [SingleLineComment("#")] COMMENT = 1236, + + #endregion + + #region fstring 100 -> + + [Push("fstringExpression")] [Mode("fstring")] [Sugar("{")] + OPEN_FSTRING_EXPPRESSION = 100, + + [Pop] [Mode("fstringExpression")] [Sugar("}")] + CLOSE_FSTRING_EXPPRESSION = 101, + [Sugar("$\"")] + [Push("fstring")] + OPEN_FSTRING, + + [Sugar("\"")] + [Mode("fstring")] + [Pop] + CLOSE_FSTRING, + + + [Mode("fstring")] + [UpTo("{","\"")] + FSTRING_CONTENT + + #endregion + + + } } \ No newline at end of file diff --git a/src/samples/while/compiler/ExpressionTyper.cs b/src/samples/while/compiler/ExpressionTyper.cs index a5f6fc8b..81f2d0d2 100644 --- a/src/samples/while/compiler/ExpressionTyper.cs +++ b/src/samples/while/compiler/ExpressionTyper.cs @@ -29,6 +29,11 @@ public WhileType TypeExpression(Expression expr, CompilerContext context) return varType; } + if (expr is FString fstring) + { + return WhileType.STRING; + } + throw new SignatureException($"unknow expression type ({expr.GetType().Name})"); } diff --git a/src/samples/while/model/FString.cs b/src/samples/while/model/FString.cs new file mode 100644 index 00000000..31c43594 --- /dev/null +++ b/src/samples/while/model/FString.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Text; +using csly.whileLang.compiler; +using Sigil; +using sly.lexer; + +namespace csly.whileLang.model; + +public class FString : Expression +{ + public LexerPosition Position { get; set; } + public Scope CompilerScope { get; set; } + + public List Elements { get; set; } + + public FString(List elements, LexerPosition position) + { + this.Elements = elements; + Position = position; + } + + public string Dump(string tab) + { + StringBuilder builder = new StringBuilder(); + builder.Append($"{{tab}}\""); + foreach (var element in Elements) + { + builder.Append(element.Dump("")); + } + + builder.Append("\""); + return builder.ToString(); + } + + public string Transpile(CompilerContext context) + { + throw new NotImplementedException(); + } + + public Emit> EmitByteCode(CompilerContext context, Emit> emiter) + { + if (Elements.Count == 1) + { + return Elements[0].EmitByteCode(context, emiter); + } + + return null; + } + + public WhileType Whiletype { get; set; } = WhileType.STRING; +} \ No newline at end of file diff --git a/src/samples/while/model/FStringElement.cs b/src/samples/while/model/FStringElement.cs new file mode 100644 index 00000000..bcfa2017 --- /dev/null +++ b/src/samples/while/model/FStringElement.cs @@ -0,0 +1,63 @@ +using System; +using csly.whileLang.compiler; +using Sigil; +using sly.lexer; + +namespace csly.whileLang.model; + +public class FStringElement : WhileAST +{ + + public LexerPosition Position { get; set; } + + public Scope CompilerScope { get; set; } + public Variable VariableElement { get; set; } + + public StringConstant StringElement { get; set; } + + public bool IsStringElement => StringElement != null; + + public bool IsVariable => VariableElement != null; + + public FStringElement(Variable variableElement, LexerPosition position) + { + VariableElement = variableElement; + Position = position; + } + + public FStringElement(StringConstant stringElement, LexerPosition position) + { + StringElement = stringElement; + Position = position; + } + + + public string Dump(string tab) + { + if (IsStringElement) + { + return tab + StringElement.Value; + } + else + { + return $"{{tab}}{{{{{VariableElement.Dump("")}}}"; + } + } + + public string Transpile(CompilerContext context) + { + throw new NotImplementedException(); + } + + public Emit> EmitByteCode(CompilerContext context, Emit> emiter) + { + if (IsStringElement) + { + return StringElement.EmitByteCode(context, emiter); + } + else + { + return VariableElement.EmitByteCode(context, emiter); + } + } +} \ No newline at end of file diff --git a/src/sly/lexer/fsm/FSMLexer.cs b/src/sly/lexer/fsm/FSMLexer.cs index 2e6d5613..7bd3513b 100644 --- a/src/sly/lexer/fsm/FSMLexer.cs +++ b/src/sly/lexer/fsm/FSMLexer.cs @@ -314,18 +314,22 @@ private FSMMatch ConsumeIndents3(ReadOnlyMemory source, LexerPosition l { case LexerIndentationType.Indent: { + var position = lexerPosition.Clone(); + position.IsPush = false; + position.IsPop = false; + position.Mode = null; var indent = FSMMatch.Indent(lexerPosition.Indentation.CurrentLevel); indent.Result = new Token { IsIndent = true, IsUnIndent = false, IsNoIndent = false, - Position = lexerPosition.Clone() + Position = position }; indent.IsNoIndent = false; indent.IsIndent = true; indent.IsUnIndent = false; - indent.NewPosition = lexerPosition.Clone(); + indent.NewPosition = position; indent.NewPosition.Index += currentShift.Length; indent.NewPosition.Column += currentShift.Length; return indent; @@ -333,17 +337,21 @@ private FSMMatch ConsumeIndents3(ReadOnlyMemory source, LexerPosition l case LexerIndentationType.UIndent: { var uIndent = FSMMatch.UIndent(lexerPosition.Indentation.CurrentLevel); + var position = lexerPosition.Clone(); + position.IsPush = false; + position.IsPop = false; + position.Mode = null; uIndent.Result = new Token { IsIndent = false, IsUnIndent = true, IsNoIndent = false, - Position = lexerPosition.Clone() + Position = position }; uIndent.IsNoIndent = false; uIndent.IsIndent = false; uIndent.IsUnIndent = true; - uIndent.NewPosition = lexerPosition.Clone(); + uIndent.NewPosition = position; uIndent.NewPosition.Index += currentShift.Length; uIndent.NewPosition.Column += currentShift.Length; return uIndent; diff --git a/src/sly/lexer/fsm/FSMMatch.cs b/src/sly/lexer/fsm/FSMMatch.cs index ad54fd62..f8a06538 100644 --- a/src/sly/lexer/fsm/FSMMatch.cs +++ b/src/sly/lexer/fsm/FSMMatch.cs @@ -66,6 +66,8 @@ public static FSMMatch Indent(int level) IsIndent = true, IsSuccess = true, IndentationLevel = level, + IsPop = false, + IsPush = false, Result = new Token {IsIndent = true, IsEOS = false} }; } @@ -76,6 +78,8 @@ public static FSMMatch UIndent(int level, int count = 1) { IsUnIndent = true, IsSuccess = true, + IsPop = false, + IsPush = false, IndentationLevel = level, Result = new Token {IsUnIndent = true, IsEOS = false}, UnIndentCount = count diff --git a/tests/ParserTests/samples/IndentedWhileTests.cs b/tests/ParserTests/samples/IndentedWhileTests.cs index 795261ec..d360bd89 100644 --- a/tests/ParserTests/samples/IndentedWhileTests.cs +++ b/tests/ParserTests/samples/IndentedWhileTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using csly.indentedWhileLang.compiler; using csly.indentedWhileLang.parser; using csly.whileLang.interpreter; @@ -128,8 +129,8 @@ public void TestFactorialProgramExecAsIL() i:=1 while i < 11 do r := r * i - print ""r="".r - print ""i="".i + print $""r="".r + print $""i="".i i := i + 1 return r"; var compiler = new IndentedWhileCompiler(); @@ -147,9 +148,9 @@ public void TestIfThenElse() var program = @" # TestIfThenElse if true then - a := ""hello"" + a := $""hello"" else - b := ""world"" + b := $""world"" "; var result = parser.Parse(program); Check.That(result).IsOkParsing(); @@ -169,8 +170,15 @@ public void TestIfThenElse() Check.That(thenBlock.Get(0)).IsInstanceOf(); var thenAssign = thenBlock.Get(0) as AssignStatement; Check.That(thenAssign.VariableName).IsEqualTo("a"); - Check.That(thenAssign.Value).IsInstanceOf(); - Check.That((thenAssign.Value as StringConstant).Value).IsEqualTo("hello"); + Check.That(thenAssign.Value).IsInstanceOf(); + var fstring = thenAssign.Value as FString; + Check.That(fstring).IsNotNull(); + Check.That(fstring.Elements).CountIs(1); + Check.That(fstring.Elements[0]).IsInstanceOf(); + var element = fstring.Elements[0] as FStringElement; + Check.That(element).IsNotNull(); + Check.That(element.IsStringElement).IsTrue(); + Check.That(element.StringElement.Value).IsEqualTo("hello"); Check.That(si.ElseStmt).IsInstanceOf(); var elseBlock = si.ElseStmt as SequenceStatement; @@ -178,8 +186,15 @@ public void TestIfThenElse() Check.That(elseBlock.Get(0)).IsInstanceOf(); var elseAssign = elseBlock.Get(0) as AssignStatement; Check.That(elseAssign.VariableName).IsEqualTo("b"); - Check.That(elseAssign.Value).IsInstanceOf(); - Check.That((elseAssign.Value as StringConstant).Value).IsEqualTo("world"); + Check.That(elseAssign.Value).IsInstanceOf(); + fstring = elseAssign.Value as FString; + Check.That(fstring).IsNotNull(); + Check.That(fstring.Elements).CountIs(1); + Check.That(fstring.Elements[0]).IsInstanceOf(); + element = fstring.Elements[0] as FStringElement; + Check.That(element).IsNotNull(); + Check.That(element.IsStringElement).IsTrue(); + Check.That(element.StringElement.Value).IsEqualTo("world"); } [Fact] @@ -195,7 +210,7 @@ public void TestNestedIfThenElse() a := 2 else a := 3 - b := ""world"" + b := $""world"" return a "; var compiler = new IndentedWhileCompiler(); @@ -206,6 +221,44 @@ public void TestNestedIfThenElse() } + [Fact] + public void TestFString() + { + var buildResult = buildParser(); + var parser = buildResult.Result; + var program = @" +# fstring +v1 := 1 +v2 := 2 +fstring := $""{v1} - content - {v2} - end"" +"; + Console.WriteLine("=================================="); + Console.WriteLine("=== parse fstring"); + Console.WriteLine("=================================="); + Console.WriteLine(); + var result = parser.Parse(program); + Check.That(result).IsOkParsing(); + Check.That(result.Result).IsNotNull(); + Check.That(result.Result).IsInstanceOf(); + SequenceStatement seq = result.Result as SequenceStatement; + Check.That(seq.Count).IsEqualTo(3); + var fstringAssign = seq.Get(2) as AssignStatement; + Check.That(fstringAssign).IsNotNull(); + Check.That(fstringAssign.VariableName).IsEqualTo("fstring"); + Check.That(fstringAssign.Value).IsInstanceOf(); + var fString = fstringAssign.Value as FString; + Check.That(fString).IsNotNull(); + Check.That(fString.Elements).CountIs(4); + Check.That(fString.Elements[0]).IsInstanceOf(); + Check.That((fString.Elements[0] as FStringElement).IsVariable); + Check.That(fString.Elements[1]).IsInstanceOf(); + Check.That((fString.Elements[1] as FStringElement).IsStringElement); + Check.That(fString.Elements[2]).IsInstanceOf(); + Check.That((fString.Elements[2] as FStringElement).IsVariable); + Check.That(fString.Elements[3]).IsInstanceOf(); + Check.That((fString.Elements[3] as FStringElement).IsStringElement); + } + [Fact] public void TestInfiniteWhile() {