/SimpleBrainFuck.Core/Core/SimpleParser.cs
C# | 359 lines | 338 code | 21 blank | 0 comment | 67 complexity | 024d4ca7dde9460ef089ed22276488a3 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- using ZackFlame.SimpleBrainFuck.Core.Statments;
- using ZackFlame.SimpleBrainFuck.TextResources;
-
- namespace ZackFlame.SimpleBrainFuck.Core
- {
- public sealed class SimpleParser
- {
- private string Preprocess(string simpleCode)
- {
- if (simpleCode.Length == 0)
- return string.Empty;
-
- StringBuilder result = new StringBuilder();
- bool oneLineComment = false;
- bool literal = false;
-
- int i = 0;
- while (i < simpleCode.Length - 1)
- {
- char ch = simpleCode[i];
-
- if (!literal && ch == '/' && simpleCode[i + 1] == '/')
- {
- oneLineComment = true;
- i++;
- continue;
- }
- else if (oneLineComment)
- {
- if (ch == '\n')
- {
- oneLineComment = false;
- }
- else
- {
- i++;
- continue;
- }
- }
- else if (ch == '\"' && i != 0 && simpleCode[i - 1] != '\\')
- {
- literal = !literal;
- }
-
- result.Append(ch);
- i++;
- }
-
- if (!oneLineComment)
- result.Append(simpleCode[i]);
-
- return result.ToString();
- }
-
- private string[] GetTokens(string line)
- {
- if (line.Length == 0)
- return new string[] { };
-
- List<string> tokens = new List<string>();
- StringBuilder currentToken = new StringBuilder();
-
- const int nothing = 0, token = 1, literal = 2;
- string singleTokens = ".=";
-
- int state = nothing;
-
- int i = 0;
- while (i < line.Length)
- {
- char ch = line[i];
- bool last = (i == line.Length - 1);
-
- if ((state == token && (ch == '\"' || ch == '\\')) ||
- (state == literal && ch == '\\' && last) ||
- (state == nothing && ch == '\\'))
- {
- return null;
- }
-
- if (state == nothing)
- {
- if (ch == '\"')
- {
- state = literal;
- }
- else if (singleTokens.Contains(ch))
- {
- tokens.Add(ch.ToString());
- }
- else if (!char.IsWhiteSpace(ch))
- {
- state = token;
- currentToken.Append(ch);
- }
- }
- else if (state == token)
- {
- if (char.IsWhiteSpace(ch))
- {
- state = nothing;
- tokens.Add(currentToken.ToString());
- currentToken.Length = 0;
- }
- else if (singleTokens.Contains(ch))
- {
- state = nothing;
- tokens.Add(currentToken.ToString());
- tokens.Add(ch.ToString());
- currentToken.Length = 0;
- }
- else
- {
- currentToken.Append(ch);
- }
- }
- else // if state == literal
- {
- if (ch == '\"')
- {
- if (last)
- {
- state = nothing;
- tokens.Add(currentToken.ToString());
- }
- else
- {
- char next = line[i + 1];
- if (char.IsWhiteSpace(next) || singleTokens.Contains(next))
- {
- state = nothing;
- tokens.Add(currentToken.ToString());
- currentToken.Length = 0;
- }
- else
- {
- return null;
- }
- }
- }
- else if (ch == '\\')
- {
- char unescaped;
- if (!TryUnescapeChar(line[i + 1], out unescaped))
- return null;
-
- currentToken.Append(unescaped);
- i++;
- }
- else
- {
- currentToken.Append(ch);
- }
- }
-
- i++;
- }
-
- if (state == token)
- tokens.Add(currentToken.ToString());
- else if (state == literal)
- return null;
-
- return tokens.ToArray();
- }
-
- private bool TryUnescapeChar(char escaped, out char value)
- {
- switch (escaped)
- {
- case 'n':
- value = '\n';
- return true;
-
- case 't':
- value = '\t';
- return true;
-
- case '"':
- value = '\"';
- return true;
-
- case '\\':
- value = '\\';
- return true;
-
- case '0':
- value = '\0';
- return true;
-
- default:
- value = '\0';
- return false;
- }
- }
-
- public static BrainArg ParseParameter(string token)
- {
- if (token.Length > 1 && token[0] == '@')
- {
- var match = BrainMacros.NameRegex.Match(token.Substring(1));
-
- if (match.Success)
- {
- return new BrainMacros()
- {
- Name = match.Groups[2].Value,
- Negative = match.Groups[1].Success
- };
- }
- }
-
- return new BrainArg() { Value = token };
- }
-
- public IEnumerable<Statment> BuildSemanticTree(string simpleCode)
- {
- simpleCode = Preprocess(simpleCode.Replace(Environment.NewLine, "\n"));
- string[] lines = simpleCode.Split(new[] { '\n' },
- StringSplitOptions.None);
-
- var body = new LinkedList<Statment>();
- var bodyStack = new Stack<LinkedList<Statment>>();
-
- for (int i = 0; i < lines.Length; i++)
- {
- string[] tokens = GetTokens(lines[i]);
- if (tokens == null)
- throw new CodeErrorException(i, CodeErrors.Preprocess_InvalidLine);
-
- if (tokens.Length > 0)
- {
- string first = tokens[0];
-
- if (first == "end")
- {
- if (bodyStack.Count == 0)
- throw new CodeErrorException(i, CodeErrors.General_SuddenCloseOfStatment);
-
- body = bodyStack.Pop();
- }
- else if (first == "def")
- {
- if (tokens.Length < 2)
- throw new CodeErrorException(i, CodeErrors.Def_IncorrectDeclaration);
-
- BrainDef function = new BrainDef() { Name = tokens[1], Line = i };
- for (int j = 2; j < tokens.Length; j++)
- {
- function.MacrosArgs.Add(
- new BrainMacros() { Name = tokens[j] });
- }
-
- body.AddLast(function);
- bodyStack.Push(body);
- body = function.Statments;
- }
- else if (first == "if")
- {
- if (tokens.Length != 2)
- throw new CodeErrorException(i, CodeErrors.If_IncorrectDeclaration);
-
- BrainIf brainIf = new BrainIf() { Line = i };
- brainIf.Parameter = ParseParameter(tokens[1]);
-
- body.AddLast(brainIf);
- bodyStack.Push(body);
- body = brainIf.IfStatments;
- }
- else if (first == "else")
- {
- Statment statment = bodyStack.Peek().Last();
- BrainIf brainIf = statment as BrainIf;
- if (brainIf == null)
- {
- throw new CodeErrorException(i, CodeErrors.If_IfNotDeclarated);
- }
- else
- {
- body = brainIf.ElseStatments;
- }
- }
- else if (first.StartsWith("$"))
- {
- if (first.Length < 2)
- throw new CodeErrorException(i, CodeErrors.Call_IncorrectCall);
-
- string defName = first.Substring(1);
- BrainCall brainCall = new BrainCall() { DefName = defName, Line = i };
- for (int j = 1; j < tokens.Length; j++)
- {
- brainCall.CallArgs.Add(ParseParameter(tokens[j]));
- }
-
- body.AddLast(brainCall);
- }
- else if (first.StartsWith("@"))
- {
- if (first.Length < 2 ||
- tokens.Length < 3 || tokens.Length > 5 ||
- tokens[1] != "=")
- {
- throw new CodeErrorException(i, CodeErrors.Assignment_IncorrectAssignment);
- }
-
- BrainMacros newMacros = (BrainMacros)ParseParameter(tokens[0]);
- if (newMacros.Negative)
- throw new CodeErrorException(i, CodeErrors.Assignment_CantBeNegative);
-
- var brainExpression = new BrainMacrosExpression();
- switch (tokens.Length - 2) // tokens after '='
- {
- case 1:
- brainExpression.Left = ParseParameter(tokens[2]);
- break;
-
- case 2:
- brainExpression.Left = ParseParameter(tokens[3]);
- brainExpression.Operator = tokens[2];
- break;
-
- case 3:
- brainExpression.Left = ParseParameter(tokens[2]);
- brainExpression.Operator = tokens[3];
- brainExpression.Right = ParseParameter(tokens[4]);
- break;
- }
-
- body.AddLast(new BrainMacrosAssignment()
- {
- Name = newMacros.Name,
- Expression = brainExpression,
- Line = i,
- });
- }
- else
- {
- BrainPureCode code = new BrainPureCode()
- {
- Value = lines[i].Trim(' ', '\t'),
- Line = i
- };
- body.AddLast(code);
- }
- }
- }
-
- if (bodyStack.Count > 0)
- throw new CodeErrorException(lines.Length - 1, CodeErrors.General_UnclosedStatment);
-
- return body;
- }
- }
- }