Created
August 22, 2020 19:29
-
-
Save velddev/a01e880ac7a02dea02c84729ea01e251 to your computer and use it in GitHub Desktop.
playing with interpreters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Text.RegularExpressions; | |
namespace random_interpreter | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var tokenizer = new Tokenizer(); | |
var parser = new Parser(); | |
while(true) | |
{ | |
try | |
{ | |
var input = Console.ReadLine(); | |
var tokens = tokenizer.TokenizeString(input); | |
parser.Parse(tokens); | |
} | |
catch(Exception e) | |
{ | |
Console.WriteLine(e.ToString()); | |
} | |
} | |
} | |
} | |
public enum TokenType | |
{ | |
Declare, | |
Identifier, | |
Assigns, | |
String, | |
Number, | |
Boolean, | |
EndStatement, | |
EndScript | |
} | |
public struct Token { | |
public TokenType type; | |
public string value; | |
public override string ToString() | |
{ | |
return $"[{type}] ({value})"; | |
} | |
} | |
class Tokenizer | |
{ | |
private List<Tuple<string, TokenType>> availableTokens = new List<Tuple<string, TokenType>> | |
{ | |
new Tuple<string, TokenType>("^var", TokenType.Declare), | |
new Tuple<string, TokenType>("^=", TokenType.Assigns), | |
new Tuple<string, TokenType>("^(true|false)", TokenType.Boolean), | |
new Tuple<string, TokenType>("^([\\d\\.]+)", TokenType.Number), | |
new Tuple<string, TokenType>("^\"(.*)\"", TokenType.String), | |
new Tuple<string, TokenType>("^([a-zA-Z][\\w]*)", TokenType.Identifier), | |
new Tuple<string, TokenType>("^;", TokenType.EndStatement) | |
}; | |
public List<Token> TokenizeString(string str) | |
{ | |
var tokens = new List<Token>(); | |
while(str.Length != 0) | |
{ | |
foreach(var token in availableTokens) | |
{ | |
var match = Regex.Match(str, token.Item1); | |
if(match.Success) | |
{ | |
tokens.Add( | |
new Token | |
{ | |
type = token.Item2, | |
value = match.Groups.Count > 1 | |
? match.Groups[1].Value | |
: null | |
}); | |
str = str.Substring(match.Value.Length); | |
continue; | |
} | |
} | |
if(str.Length == 0) | |
{ | |
break; | |
} | |
str = str.Substring(1); | |
} | |
tokens.Add(new Token { type = TokenType.EndScript }); | |
return tokens; | |
} | |
} | |
public static class ParserUtils | |
{ | |
public static void Upsert<TKey, TValue>( | |
this Dictionary<TKey, TValue> dict, TKey key, TValue value) | |
{ | |
if (dict.ContainsKey(key)) | |
{ | |
dict[key] = value; | |
} | |
else | |
{ | |
dict.Add(key, value); | |
} | |
} | |
public static (bool, string) Accept(this List<Token> tokenList, TokenType type) | |
{ | |
if(tokenList.Count == 0) | |
{ | |
return (false, null); | |
} | |
if(tokenList[0].type == type) | |
{ | |
var value = tokenList[0].value; | |
tokenList.RemoveAt(0); | |
return (true, value); | |
} | |
return (false, null); | |
} | |
public static string Expect(this List<Token> tokenList, TokenType type) | |
{ | |
var accept = tokenList.Accept(type); | |
if(accept.Item1) | |
{ | |
return accept.Item2; | |
} | |
throw new InvalidOperationException( | |
$"invalid token, expected {type}, but received {tokenList[0].type}."); | |
} | |
} | |
class Parser | |
{ | |
object Expression(List<Token> tokens, Dictionary<string, object> values) | |
{ | |
var (stringValid, value) = tokens.Accept(TokenType.String); | |
if(stringValid) | |
{ | |
return value; | |
} | |
var (numberValid, numberValue) = tokens.Accept(TokenType.Number); | |
if(numberValid) | |
{ | |
return double.Parse(numberValue); | |
} | |
var (boolValid, boolValue) = tokens.Accept(TokenType.Boolean); | |
if(boolValid) | |
{ | |
return bool.Parse(boolValue); | |
} | |
var (identifierValid, identifierValue) = tokens.Accept(TokenType.Boolean); | |
if (identifierValid) | |
{ | |
return values[identifierValue]; | |
} | |
throw new InvalidOperationException("Expected an expression, but received none"); | |
} | |
void Statement(List<Token> tokens, Dictionary<string, object> values) | |
{ | |
var (declareValid, _) = tokens.Accept(TokenType.Declare); | |
if(declareValid) | |
{ | |
var identifier = tokens.Expect(TokenType.Identifier); | |
AssignValue(identifier, tokens, values); | |
} | |
var (identifierValid, identifierValue) = tokens.Accept(TokenType.Identifier); | |
if(identifierValid) | |
{ | |
AssignValue(identifierValue, tokens, values); | |
} | |
} | |
void AssignValue(string identifier, List<Token> tokens, Dictionary<string, object> values) | |
{ | |
tokens.Expect(TokenType.Assigns); | |
var value = Expression(tokens, values); | |
tokens.Expect(TokenType.EndStatement); | |
values.Upsert(identifier, value); | |
} | |
void Body(List<Token> tokens, Dictionary<string, object> values) | |
{ | |
Statement(tokens, values); | |
} | |
public void Parse(List<Token> tokens) | |
{ | |
var context = new Dictionary<string, object>(); | |
Body(tokens, context); | |
foreach(var o in context) | |
{ | |
Console.WriteLine($"{o.Key} = {o.Value}"); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment