Skip to content

Commit

Permalink
working
Browse files Browse the repository at this point in the history
  • Loading branch information
mk3008 committed Nov 8, 2024
1 parent fad50e4 commit 24cac05
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/Carbunql.LexicalAnalyzer/LexType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public enum LexType : byte

WildCard,
Value,
SchemaOrTable,
Namespace,
SchemaOrTableOrColumn,
Column,
Function,
Expand Down
136 changes: 103 additions & 33 deletions src/Carbunql.LexicalAnalyzer/Lexer.Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,58 @@ namespace Carbunql.LexicalAnalyzer;

public static partial class Lexer
{
/// <summary>
/// Defines a set of characters considered as symbols that terminate an identifier.
/// </summary>
private static readonly HashSet<char> Symbols = new HashSet<char>
{
'+', '-', '*', '/', '%', // Arithmetic operators
'(', ')', '[', ']', '{', '}', // Brackets and braces
'~', '@', '#', '$', '^', '&', // Special symbols
'!', '?', ':', ';', ',', '.', '<', '>', '=', '|', '\\', // Other symbols
'`', '"', '\'' // Quotation marks
};

/// <summary>
/// Attempts to find the end position of a valid identifier starting at a given position in the specified memory segment.
/// </summary>
/// <param name="memory">The memory segment containing the characters to analyze.</param>
/// <param name="startPosition">The initial position to start searching for the identifier.</param>
/// <param name="position">
/// The position marking the end of the identifier if found, or the start position if the search fails.
/// </param>
/// <returns>
/// <c>true</c> if a valid identifier was found; <c>false</c> if the identifier is invalid (e.g., starts with a digit).
/// </returns>
public static bool TryGetCharacterEndPosition(ReadOnlyMemory<char> memory, in int startPosition, out int position)
{
position = startPosition;

memory.SkipWhiteSpacesAndComment(ref position);

// Disallow identifiers that start with a digit.
if (char.IsDigit(memory.Span[position]))
{
return false;
}

// Loop until the end of the identifier or a symbol/whitespace is encountered.
while (!memory.IsAtEnd(position))
{
var current = memory.Span[position];
if (char.IsWhiteSpace(current) || Symbols.Contains(current))
{
break;
}
position++;
}

// Return false if no valid identifier was found.
return (position != startPosition);
}



public static IEnumerable<Lex> ReadExpressionLexes(ReadOnlyMemory<char> memory, int position)
{
memory.SkipWhiteSpacesAndComment(ref position);
Expand All @@ -23,12 +75,24 @@ public static IEnumerable<Lex> ReadExpressionLexes(ReadOnlyMemory<char> memory,
break;
}

// value
if (TryParseSingleQuotedText(memory, ref position, out lex)
|| TryParseNumericValue(memory, ref position, out lex)
|| TryParseSpecialValue(memory, ref position, out lex))
// paren
if (TryParseLeftParen(memory, ref position, out lex))
{
yield return lex;
memory.SkipWhiteSpacesAndComment(ref position);

// 次のトークンがselectの場合、インラインクエリのため特殊

// それ以外は再帰処理
foreach (var innerLex in ReadExpressionLexes(memory, position))
{
yield return innerLex;
position = innerLex.EndPosition;
}

// close paren
lex = ParseRightParen(memory, ref position);
yield return lex;

// operator check
memory.SkipWhiteSpacesAndComment(ref position);
Expand All @@ -43,25 +107,11 @@ public static IEnumerable<Lex> ReadExpressionLexes(ReadOnlyMemory<char> memory,
break;
}

// column
if (TryParseSchemaOrTableOrColumn(memory, ref position, out lex))
// value
if (TryParseSingleQuotedText(memory, ref position, out lex)
|| TryParseNumericValue(memory, ref position, out lex))
//|| TryParseSpecialValue(memory, ref position, out lex))
{
while (lex.Type == LexType.SchemaOrTable)
{
yield return lex;

lex = ParseIdentifierSeparator(memory, ref position);
yield return lex;

if (!TryParseSchemaOrTableOrColumn(memory, ref position, out lex))
{
throw new FormatException();
}
}
if (lex.Type != LexType.Column)
{
throw new FormatException();
}
yield return lex;

// operator check
Expand All @@ -77,21 +127,39 @@ public static IEnumerable<Lex> ReadExpressionLexes(ReadOnlyMemory<char> memory,
break;
}

if (TryParseLeftParen(memory, ref position, out lex))
// 一旦読み込む
var value = ReadCharactorIdentifier(memory, ref position);

//identifierが特殊文字と一致するなら特殊文字
//次がカッコなら関数
//次が.ならnamespace
//それ以外は列

// function
if (TryParseFunction(memory, ref position, out lex))
{
yield return lex;
memory.SkipWhiteSpacesAndComment(ref position);

// 次のトークンがselectの場合、インラインクエリのため特殊
}

// それ以外は再帰処理
foreach (var innerLex in ReadExpressionLexes(memory, position))
// column
if (TryParseNamespaceOrColumn(memory, ref position, out lex))
{
while (lex.Type == LexType.Namespace)
{
yield return innerLex;
position = innerLex.EndPosition;
}
yield return lex;

lex = ParseRightParen(memory, ref position);
lex = ParseIdentifierSeparator(memory, ref position);
yield return lex;

if (!TryParseNamespaceOrColumn(memory, ref position, out lex))
{
throw new FormatException();
}
}
if (lex.Type != LexType.Column)
{
throw new FormatException();
}
yield return lex;

// operator check
Expand All @@ -106,6 +174,8 @@ public static IEnumerable<Lex> ReadExpressionLexes(ReadOnlyMemory<char> memory,
// alias, expression separator, or 'from' keyword
break;
}


}
}

Expand All @@ -123,7 +193,7 @@ public static bool TryParseExpressionName(ReadOnlyMemory<char> memory, ref int p
SkipAliasCommand(memory, ref position);
memory.SkipWhiteSpacesAndComment(ref position);

if (TryParseSchemaOrTableOrColumn(memory, ref position, out lex))
if (TryParseNamespaceOrColumn(memory, ref position, out lex))
{
if (lex.Type == LexType.Column)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Carbunql.LexicalAnalyzer/Lexer.Select.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Carbunql.LexicalAnalyzer;
public static partial class Lexer
{
[MemberNotNullWhen(true)]
internal static bool TryParseSelectLex(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
internal static bool TryParseSelect(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
{
lex = default;

Expand Down
6 changes: 3 additions & 3 deletions src/Carbunql.LexicalAnalyzer/Lexer.TableColumnIdentifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Carbunql.LexicalAnalyzer;
public static partial class Lexer
{
[MemberNotNullWhen(true)]
internal static bool TryParseSchemaOrTableOrColumn(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
internal static bool TryParseNamespaceOrColumn(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
{
lex = default;
if (TryParseSchemaOrTableOrColumnAsDefault(memory, ref position, out lex)) return true;
Expand Down Expand Up @@ -49,7 +49,7 @@ private static bool TryParseSchemaOrTableOrColumnAsDefault(ReadOnlyMemory<char>
// Check for the dot separator
if (next == '.')
{
lex = new Lex(memory, LexType.SchemaOrTable, start, position - start);
lex = new Lex(memory, LexType.Namespace, start, position - start);
return true; // Successfully parsed schema or table
}

Expand Down Expand Up @@ -108,7 +108,7 @@ private static bool TryParseSchemaOrTableOrColumnAsWithDelimiters(ReadOnlyMemory
// Check if the next character is a dot
if (position < memory.Length && memory.Span[position] == '.')
{
lex = new Lex(memory, LexType.SchemaOrTable, start, position - start);
lex = new Lex(memory, LexType.Namespace, start, position - start);
return true; // Successfully parsed schema or table
}
else
Expand Down
2 changes: 1 addition & 1 deletion src/Carbunql.LexicalAnalyzer/Lexer.With.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Carbunql.LexicalAnalyzer;
public static partial class Lexer
{
[MemberNotNullWhen(true)]
internal static bool TryParseWithOrRecursiveLex(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
internal static bool TryParseWithOrRecursive(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
{
lex = default;

Expand Down
35 changes: 27 additions & 8 deletions src/Carbunql.LexicalAnalyzer/Lexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,43 @@ namespace Carbunql.LexicalAnalyzer;

public static partial class Lexer
{
public static Lex ParseSelectExpressionDelimiter(ReadOnlyMemory<char> memory, ref int position)
public static Lex ParseFrom(ReadOnlyMemory<char> memory, ref int position)
{
memory.SkipWhiteSpacesAndComment(ref position);

Lex lex;
if (memory.TryParseSingleCharLex(ref position, ',', LexType.ExpressionSeparator, out lex))
if (memory.TryParseKeywordIgnoreCase(ref position, "from", LexType.From, out var lex))
{
return lex;
}
throw new FormatException();
}

if (memory.TryParseKeywordIgnoreCase(ref position, "from", LexType.From, out lex))
{
return lex;
}
public static bool TryParseExpressionSeparator(ReadOnlyMemory<char> memory, ref int position, out Lex lex)
{
memory.SkipWhiteSpacesAndComment(ref position);

throw new FormatException("Expected a comma or the keyword 'FROM' to end the SELECT clause.");
if (memory.TryParseSingleCharLex(ref position, ',', LexType.ExpressionSeparator, out lex)) return true;
return false;
}

//public static Lex ParseSelectExpressionDelimiter(ReadOnlyMemory<char> memory, ref int position)
//{
// memory.SkipWhiteSpacesAndComment(ref position);

// Lex lex;
// if (memory.TryParseSingleCharLex(ref position, ',', LexType.ExpressionSeparator, out lex))
// {
// return lex;
// }

// if (memory.TryParseKeywordIgnoreCase(ref position, "from", LexType.From, out lex))
// {
// return lex;
// }

// throw new FormatException("Expected a comma or the keyword 'FROM' to end the SELECT clause.");
//}


//public static Lex TokenizeAsQueryStart(ReadOnlyMemory<char> memory)
//{
Expand Down
77 changes: 50 additions & 27 deletions src/Carbunql.LexicalAnalyzer/SelectQueryParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,65 @@

public static class SelectQueryParser
{
public static object Parse(string sql)
// debug
public static IEnumerable<Lex> Parse(string sql)
{
//var memory = sql.AsMemory();
//var position = 0;
var memory = sql.AsMemory();
var position = 0;

//memory.SkipWhiteSpaces(ref position);
Lex lex;

//Lexer.SkipComment(memory, ref position);
// "with"
if (Lexer.TryParseWithOrRecursive(memory, ref position, out lex))
{
throw new FormatException();
}

//Lexer.SkipWhiteSpaces(memory, ref position);
if (!Lexer.TryParseSelect(memory, ref position, out lex))
{
throw new FormatException();
}

//Lex lex;
//if (Lexer.TryParseWithOrRecursiveLex(memory, ref position, out lex))
//{
// throw new NotImplementedException();
//}
//else if (Lexer.TryParseSelectLex(memory, ref position, out lex))
//{
// // value
// "select"
yield return lex;

//}
if (lex.Type == LexType.SelectDistinctOn)
{
// paren argument
throw new FormatException();
}

throw new FormatException();
}

//private static object ParseSelectClause(ReadOnlyMemory<char> memory, ref int position)
//{
// Lex lex;
bool hasExpression = false;
while (true)
{
// expression
foreach (var item in Lexer.ReadExpressionLexes(memory, position))
{
hasExpression = true;
position = item.EndPosition;
yield return item;
}
// alias name
if (Lexer.TryParseExpressionName(memory, ref position, out lex))
{
yield return lex;
}

// var lexes =
//expression Separator
if (!Lexer.TryParseExpressionSeparator(memory, ref position, out lex))
{
break;
}
}

if (hasExpression == false)
{
throw new FormatException();
}

// if (Lexer.TryParseValueLex(memory, ref position, out lex))
// {
// "from"
lex = Lexer.ParseFrom(memory, ref position);


// }
//}
// table
}
}
Loading

0 comments on commit 24cac05

Please sign in to comment.