Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Commit

Permalink
Merge pull request #8 from DemonExposer/functions
Browse files Browse the repository at this point in the history
Functions
  • Loading branch information
DemonExposer authored Jul 19, 2022
2 parents 303592e + 780d4d0 commit 62aaca5
Show file tree
Hide file tree
Showing 45 changed files with 377 additions and 245 deletions.
6 changes: 3 additions & 3 deletions BuiltinFunctions/Print.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using System.Text;
using Interpreter.Tokens;
using Interpreter.Tokens.Operators.N_Ary;
using Interpreter.Types.Function;
using Object = Interpreter.Types.Object;
using TrieDictionary;

namespace Interpreter.BuiltinFunctions;

public class Print : FunctionBody {
public Print(Token[] expressions) : base(expressions) { }
public Print(MultilineStatementOperator expressions) : base(expressions) { }

public override Object Execute(Object[] args, TrieDictionary<Object> vars) {
public override Object Execute(Object[] args, TrieDictionary<Object> vars, List<TrieDictionary<Object>> topScopeVars) {
StringBuilder sb = new StringBuilder();
foreach (Object o in args)
sb.Append(o.ToString()).Append(" ");
Expand Down
2 changes: 1 addition & 1 deletion Lexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace Interpreter;

public class Lexer {
// Regex matching all valid strings, with the least complicated in the back so that e.g. == gets matched as == and not as =, =
public static CheckedString[] Lex(string line, int lineNo) => Regex.Matches(line, "([a-zA-Z0-9]+|==|!=|\\|\\||&&|>=|<=|[\\^*/%+-=(){}#<>!])").ToList().Select(match => new CheckedString {Str = match.Value.Trim(), Line = lineNo}).ToArray();
public static CheckedString[] Lex(string line, int lineNo) => Regex.Matches(line, "([a-zA-Z0-9]+|==|!=|\\|\\||&&|>=|<=|[\\^*/%+-=(){}#<>!,])").ToList().Select(match => new CheckedString {Str = match.Value.Trim(), Line = lineNo}).ToArray();
}
183 changes: 87 additions & 96 deletions Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
using Interpreter.Tokens.Operators.Binary.Boolean;
using Interpreter.Tokens.Operators.N_Ary;
using Interpreter.Tokens.Operators.Unary;
using Interpreter.Tokens.Statements;
using Interpreter.Tokens.Separators;
using Interpreter.Tokens.Statements.Binary;
using Interpreter.Tokens.Statements.Unary;
using TrieDictionary;
using Object = Interpreter.Types.Object;

namespace Interpreter;

public class Parser {
public static int GetTopElementIndex(Token[] line, int startIndex, bool isRightBound) {
if (line[startIndex] is Statement)
if (line[startIndex] is BinaryStatement)
return startIndex;

int highestPriorityNum = -1;
int index = -1;
for (int i = startIndex; i < line.Length && i >= 0; i += isRightBound ? 1 : -1) {
Expand Down Expand Up @@ -62,7 +64,7 @@ public static CheckedString[] CheckComment(CheckedString[] line) {
/**
* This now practically just parses everything, so maybe some refactoring is needed
*/
private static Token SymmetricBinaryOperatorParse(Token[] line, int i, List<TrieDictionary<Object>> vars, string[] lines, ref int lineNo, int depth, bool isRightBound) {
private static Token SymmetricBinaryOperatorParse(Token[] line, int i, string[] lines, ref int lineNo, int depth, bool isRightBound) {
int startIndex = i + (isRightBound ? 1 : -1);
int j = GetTopElementIndex(line, startIndex, isRightBound);
if (j == startIndex && !isRightBound) {
Expand All @@ -80,94 +82,76 @@ private static Token SymmetricBinaryOperatorParse(Token[] line, int i, List<Trie
// BinaryOperator does not exist or is already done
if (j < 0 || line[j].IsDone)
if (!isRightBound)
return Parse(line, j + 1, vars, lines, ref lineNo, depth + 1);
return Parse(line, j + 1, lines, ref lineNo, depth + 1);
else
return Parse(line, i + 1, vars, lines, ref lineNo, depth + 1);
return Parse(line, i + 1, lines, ref lineNo, depth + 1);

// BinaryOperator does exist and is not done, parse it
return Parse(line, j, vars, lines, ref lineNo, depth + 1);
return Parse(line, j, lines, ref lineNo, depth + 1);
}

/**
* Removes parentheses and parses inside expression by identifying the top operator and calling Parse
* Removes parentheses and parses inside expression(s) by identifying the top operator and calling Parse
*/
private static Token ParenthesesParse(Token[] line, int i, List<TrieDictionary<Object>> vars, string[] lines, ref int lineNo, int depth, bool isRightBound) {
int startIndex = -1;
int highestPriorityNum = -1;
int index = -1;
Token[] subLine = {};
int numBrackets = 1;
for (int j = i + (isRightBound ? 1 : -1); numBrackets > 0; j += isRightBound ? 1 : -1) {
// Go until paired bracket is found
if (line[j].Str == ")")
numBrackets += isRightBound ? -1 : 1;
else if (line[j].Str == "(")
numBrackets += isRightBound ? 1 : -1;

if (numBrackets == 0)
break;

// If there is a nested set of brackets, add that entire set immediately, because otherwise something goes wrong
if (isRightBound ? line[j].Str == "(" : line[j].Str == ")") {
while (numBrackets > 1) {
if (isRightBound && subLine.Length == 0)
startIndex = j;
subLine = isRightBound ? subLine.Append(line[j]).ToArray() : subLine.Prepend(line[j]).ToArray();
j += isRightBound ? 1 : -1;
if (line[j].Str == "(")
numBrackets += isRightBound ? 1 : -1;
else if (line[j].Str == ")")
numBrackets += isRightBound ? -1 : 1;
}
private static Token[] ParenthesesParse(Token[] line, int i, string[] lines, ref int lineNo, int depth, bool isRightBound) {
List<Token[]> arguments = new List<Token[]>();
List<Token> subLine = new List<Token>();
int numBrackets = 0;
for (int j = i + (isRightBound ? 1 : -1); line[j].Str != (isRightBound ? ")" : "("); j += isRightBound ? 1 : -1) {
if (line[j].Str == "(")
numBrackets++;
else if (line[j].Str == ")")
numBrackets--;

while (numBrackets != 0) {
if (isRightBound)
subLine.Add(line[j]);
else
subLine = subLine.Prepend(line[j]).ToList();

j += isRightBound ? 1 : -1;

if (line[j].Str == "(")
numBrackets++;
else if (line[j].Str == ")")
numBrackets--;
}

if (isRightBound) {
if (subLine.Length == 0)
startIndex = j;
subLine = subLine.Append(line[j]).ToArray();
} else {
startIndex = j;
subLine = subLine.Prepend(line[j]).ToArray();
// When a comma is found, add the current buffer as an argument and start on a new argument
if (line[j] is CommaSeparator) {
if (isRightBound)
arguments.Add(subLine.ToArray());
else
arguments = arguments.Prepend(subLine.ToArray()).ToList();
subLine = new List<Token>();
continue;
}

// Get the index of the operator with the lowest priority (highest number) to make sure that gets parsed first
// This should really go through GetTopElementIndex
int priority = -1;
try {
priority = Program.priorities[line[j].Str];
} catch (KeyNotFoundException) { }

if (line[j] is BinaryOperator && priority != -1) {
if (isRightBound ? priority >= highestPriorityNum : priority > highestPriorityNum) {
highestPriorityNum = priority;
index = j;
}
}
if (isRightBound)
subLine.Add(line[j]);
else
subLine = subLine.Prepend(line[j]).ToList();
}

if (index == -1)
index = startIndex; // This makes sure index-startIndex is 0, because the first and only element should be parsed

if (startIndex == -1) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Error in bracket parsing");
Console.ResetColor();
}
if (subLine.Count > 0)
if (isRightBound)
arguments.Add(subLine.ToArray());
else
arguments = arguments.Prepend(subLine.ToArray()).ToList();

// Parse the data in the brackets, where index is the index of the head of the tree in line, so index-i
// (i being the starting point of subLine in line) will be the index in subLine plus one
return Parse(subLine, index - startIndex, vars, lines, ref lineNo, depth + 1);
// Parse each element in the brackets and put it in a new list
List<Token> properArguments = new List<Token>();
for (int j = 0; j < arguments.Count; j++)
properArguments.Add(Parse(arguments[j], GetTopElementIndex(arguments[j].ToArray(), 0, isRightBound), lines, ref lineNo, depth + 1));

return properArguments.ToArray();
}

/**
* Note that this is a bad implementation with too strict constraints, but for now, only functionality is important
*/
private static MultiLineStatementOperator CurlyBracketsParse(string[] lines, ref int i, List<TrieDictionary<Object>> vars, int depth) {
private static MultilineStatementOperator CurlyBracketsParse(Token[] line, string[] lines, ref int i, int depth) {
// Get copy of vars so that it doesn't get affected by method calls lower in the recursion tree
List<TrieDictionary<Object>> properVars = new List<TrieDictionary<Object>>(vars);
properVars.Add(new TrieDictionary<Object>());

MultiLineStatementOperator mso = new MultiLineStatementOperator();
List<Token> tokens = new List<Token>();
int initialIndex = i++; // immediately increment i so that this function doesn't try to parse itself, but instead the next line
int numBrackets = 1;
Expand All @@ -178,10 +162,10 @@ private static MultiLineStatementOperator CurlyBracketsParse(string[] lines, ref
if (lexedLine.Length == 0)
continue;

Token[] tokenizedLine = Tokenizer.Tokenize(lexedLine, properVars);
Token[] tokenizedLine = Tokenizer.Tokenize(lexedLine);

int before = i;
tokens.Add(Parse(tokenizedLine, GetTopElementIndex(tokenizedLine, 0, true), properVars, lines, ref i, depth + 1));
tokens.Add(Parse(tokenizedLine, GetTopElementIndex(tokenizedLine, 0, true), lines, ref i, depth + 1));

if (i != before)
continue;
Expand All @@ -199,47 +183,48 @@ private static MultiLineStatementOperator CurlyBracketsParse(string[] lines, ref
if (i >= lines.Length)
throw new FormatException("no matched bracket for bracket on line " + initialIndex);

mso.Children = tokens.ToArray();
((MultilineStatementOperator) line[^1]).Children = tokens.ToArray();

return mso;
return (MultilineStatementOperator) line[^1];
}

public static Token Parse(Token[] line, int i, List<TrieDictionary<Object>> vars, string[] lines, ref int lineNo, int depth) {
public static Token Parse(Token[] line, int i, string[] lines, ref int lineNo, int depth) {
Token t = line[i];

// Check which lowest level class (i.e. most abstract), which can be parsed uniformly, the object is an instance of
if (t is ArithmeticOperator or BooleanOperator) {
line[i].IsDone = true;

// Parse only the appropriate section (i.e. Left should only parse to the left and Right only to the right, that's what the array slicing does)
((BinaryOperator) t).Left = SymmetricBinaryOperatorParse(line.Take(i+1).ToArray(), i, vars, lines, ref lineNo, depth + 1, false);
((BinaryOperator) t).Right = SymmetricBinaryOperatorParse(new ArraySegment<Token>(line, i, line.Length - i).ToArray(), 0, vars, lines, ref lineNo, depth + 1, true);
((BinaryOperator) t).Left = SymmetricBinaryOperatorParse(line.Take(i+1).ToArray(), i, lines, ref lineNo, depth + 1, false);
((BinaryOperator) t).Right = SymmetricBinaryOperatorParse(new ArraySegment<Token>(line, i, line.Length - i).ToArray(), 0, lines, ref lineNo, depth + 1, true);
} else if (t is DeclarationOperator decOp) {
decOp.SetVars(vars);
decOp.Left = Parse(line, i + 1, vars, lines, ref lineNo, depth+1);
decOp.Left = Parse(line, i + 1, lines, ref lineNo, depth+1);

if (i + 2 < line.Length) // Only Parse right hand side if it exists
decOp.Right = Parse(line, i + 2, vars, lines, ref lineNo, depth + 1);
decOp.Right = Parse(line, i + 2, lines, ref lineNo, depth + 1);
} else if (t is AssignmentOperator assOp) {
assOp.IsDone = true;

assOp.SetVars(vars);
assOp.Left = Parse(line, i - 1, vars, lines, ref lineNo, depth+1);
assOp.Left = Parse(line, i - 1, lines, ref lineNo, depth+1);
Token[] subLine = new ArraySegment<Token>(line, i, line.Length - i).ToArray();
assOp.Right = Parse(subLine, GetTopElementIndex(subLine, 1, true), vars, lines, ref lineNo, depth+1);
assOp.Right = Parse(subLine, GetTopElementIndex(subLine, 1, true), lines, ref lineNo, depth+1);
} else if (t is ParenthesesOperator parOp) {
parOp.Child = ParenthesesParse(line, i, vars, lines, ref lineNo, depth + 1, line[i].Str == "(");
} else if (t is MinusUnaryOperator minUnOp) {
minUnOp.Child = Parse(line, i + 1, vars, lines, ref lineNo, depth + 1);
} else if (t is NotUnaryOperator notUnOp) {
notUnOp.Child = Parse(line, i + 1, vars, lines, ref lineNo, depth + 1);
} else if (t is VariableToken vt) { // TODO: make sure multiple arguments get parsed properly
parOp.Children = ParenthesesParse(line, i, lines, ref lineNo, depth + 1, line[i].Str == "(");
} else if (t is UnaryOperator unOp) {
unOp.Child = Parse(line, i + 1, lines, ref lineNo, depth + 1);
} else if (t is VariableToken vt) {
if (i + 1 < line.Length && line[i+1] is ParenthesesOperator)
vt.Args = Parse(line, i + 1, vars, lines, ref lineNo, depth + 1);
} else if (t is Statement statement) {
Token left = Parse(line, i + 1, vars, lines, ref lineNo, depth + 1);
vt.Args = Parse(line, i + 1, lines, ref lineNo, depth + 1);
} else if (t is BinaryStatement statement) {
int addition = 1;
if (t is FunctionStatement fs) {
addition = 2;
fs.Name = line[i + 1].Str;
}
Token left = Parse(line, i + addition, lines, ref lineNo, depth + 1);
if (left is not ParenthesesOperator po)
throw new FormatException("statement condition on line " + left.Line + " is missing parentheses");
throw new FormatException("statement condition/parameter declaration on line " + left.Line + " is missing parentheses");

statement.Left = po;

Expand All @@ -254,7 +239,13 @@ public static Token Parse(Token[] line, int i, List<TrieDictionary<Object>> vars
j++;
} while (numBrackets != 0);

statement.Right = CurlyBracketsParse(lines, ref lineNo, vars, depth + 1);
statement.Right = CurlyBracketsParse(line, lines, ref lineNo, depth + 1);
} else if (t is UnaryStatement unStat) {
Token child = Parse(line, i + 1, lines, ref lineNo, depth + 1);
if (child is not ParenthesesOperator po)
throw new FormatException("statement argument on line " + child.Line + " is missing parentheses");

unStat.Child = po;
}

t.Line = line[i].Line;
Expand Down
19 changes: 13 additions & 6 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
using Interpreter.Tokens.Operators.Binary.Arithmetic;
using Interpreter.Tokens.Operators.Binary.Boolean;
using Interpreter.Tokens.Operators.Unary;
using Interpreter.Tokens.Statements;
using Interpreter.Types.Function;
using Boolean = Interpreter.Types.Comparable.Boolean;
using Object = Interpreter.Types.Object;
using TrieDictionary;
using Interpreter.Tokens.Operators.N_Ary;
using Interpreter.Tokens.Separators;
using Interpreter.Tokens.Statements.Binary;
using Interpreter.Tokens.Statements.Unary;

namespace Interpreter;

Expand All @@ -34,8 +36,8 @@ public static void Main(string[] args) {
// Brackets
bindings.Insert("(", typeof(ParenthesesOperator));
bindings.Insert(")", typeof(ParenthesesOperator));
bindings.Insert("{", typeof(MultiLineStatementOperator));
bindings.Insert("}", typeof(MultiLineStatementOperator));
bindings.Insert("{", typeof(MultilineStatementOperator));
bindings.Insert("}", typeof(MultilineStatementOperator));

// Boolean logic
bindings.Insert("&&", typeof(AndBinaryOperator));
Expand All @@ -53,6 +55,11 @@ public static void Main(string[] args) {
// Statements
bindings.Insert("on", typeof(OnStatement));
bindings.Insert("while", typeof(WhileLoop));
bindings.Insert("function", typeof(FunctionStatement));
bindings.Insert("return", typeof(ReturnStatement));

// Separators
bindings.Insert(",", typeof(CommaSeparator));

// Low number for priority means a higher priority
priorities.Insert("(", 0);
Expand Down Expand Up @@ -92,17 +99,17 @@ public static void Main(string[] args) {
if (lexedLine.Length == 0)
continue;

Token[] tokenizedLine = Tokenizer.Tokenize(lexedLine, new [] {vars}.ToList());
Token[] tokenizedLine = Tokenizer.Tokenize(lexedLine);

// Console.Write("[");
// foreach (Token t in tokenizedLine)
// Console.Write("{0}, ", t.GetType());
// Console.WriteLine("]");

Token tree = Parser.Parse(tokenizedLine, Parser.GetTopElementIndex(tokenizedLine, 0, true), new [] {vars}.ToList(), lines, ref i, 0);
Token tree = Parser.Parse(tokenizedLine, Parser.GetTopElementIndex(tokenizedLine, 0, true), lines, ref i, 0);

// Console.WriteLine(tree.ToString(0));
tree.Evaluate();
tree.Evaluate(new List<TrieDictionary<Object>> {vars});
}
}
}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
<img src="img/logo.png" width=200 align="right" />
This repository contains the official interpreter for GSL (General Scripting Language).<br/>

### Installation
#### Prerequisites
- dotnet SDK

#### Dependencies
Run `dotnet restore` in the project directory to install the dependencies.

#### Execution
Run `dotnet run <input file>` to build the project and run your input file. Of course, `<input file>` should be replaced with the name of the GSL file you want to run.

### Simplicity with understanding
GSL is designed as a simple language, which is supposed to make complicated tasks easier without losing the knowledge of what the programmer is doing.<br/>

Expand Down
Loading

0 comments on commit 62aaca5

Please sign in to comment.