Skip to content

Instantly share code, notes, and snippets.

@velddev
Created August 22, 2020 19:29
Show Gist options
  • Save velddev/a01e880ac7a02dea02c84729ea01e251 to your computer and use it in GitHub Desktop.
Save velddev/a01e880ac7a02dea02c84729ea01e251 to your computer and use it in GitHub Desktop.
playing with interpreters
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