Добрый день, %username%. Сегодня мы будем писать скриптовый язык программирования на C#, а точнее его интерпретатор.
Пролог
Я изучаю C# уже год, но никак не могу его нормально выучить. Подумав чуть-чуть я понял что нужно писать что-то более сложное, и в процессе этого получать опыт. Я решил написать интерпретатор Brainfuck. Написал его я минут за 5, но он так и остался без циклов. Затем я начал придумывать свой язык, писал кучу концептов синтаксиса и т.д.
Скорее под кат!
Версия v1.0
Первая версия была не то что не удачной, а просто провалом. Не очень удобный синтаксис и BASIC-подобие было не очень хорошим тоном программирования (По моему мнению). Синтаксис был таким:
#Синтаксис первой версии языка
VISIBLE Привет, мир!
IF 5 < 10 VISIBLE 5 все еще меньше 10
Версия v2.0 — Настоящее время
Речь сегодня пойдет именно об этой версии языка. На данный момент это самый приемлимый вариант. Функциональность данной версии уступает версии 1.0, но она гораздо удобней.
Пишем? Пишем!
Ну, наконец кодинг. Сначало осмотрим синтаксис языка, и его функциональную модель.
Вид функций:
NULL --
writeline(string);
write(string);
readline[type](variable);
F --
->read(string);
->show();
->delete(string);
->create(string);
Наверное вы заметили странные F и NULL, сейчас о них подробнее.
F и NULL — типы функций, F — работа с файловой системой, NULL — не имеют родителя.
т.е. Синтаксис таков:
#Имеют родителя F, по этому использует переход ->
f->create(C:15.txt);
#Не имеют родителя, переход не нужен
writeline(Привет%c мир!);
Теперь понятно? Думаю да.
Что такое %c? Это равноценно таким штукам как n, a etc. (Уж извините, склероз, забыл как называются).
%c — ставим запятую (см. далее)
%n — новая строка
Кодинг скоро? Да, прямо сейчас!
И так кодинг которого все долго ждали.
Сейчас напишем главную функцию, она читает файл по строкам и передает строки парсеру.
using System;
using System.IO;
namespace lang
{
class MainClass
{
public static void Main (string[] args)
{
try
{
using (StreamReader sr = new StreamReader(args[0])) //Читаем файл из первого аргумента
{
string line; //Строка которую будем парсить
while ((line = sr.ReadLine()) != null) //Читаем файл по строкам
{
Parse.PARSE(line); //Наш парсер
}
}
}
catch (Exception ex) //В случае ошибки вызываем catch
{
Console.Write("Ошибка чтения: ");
Console.WriteLine(ex.Message);
}
}
}
}
Думаю тут все предельно ясно, поехали дальше.
Парсер
Итак, у нас есть Main.cs, funct.cs и Parse.cs. Это все наши классы.
Main.cs — это то что мы писали выше
Parse.cs — наш парсер строк
funct.cs — методы
Пишем парсер.
using System;
namespace lang
{
public class Parse
{
public Parse ()
{
}
public static void PARSE(string code)
{
if (code.IndexOf ('#') != 0) {
if(code.EndsWith(";")) {
int a = code.IndexOf('(');
int b = code.IndexOf(')');
string func = code.Substring(0,a);
string args = code.Substring(a+1,b-a-1);
string[] arg = args.Split(',');
func = func.Replace(" ", "");
switch(func.ToLower()) {
На этом забегать далеко не стоит, разберем по строкам.
public static void PARSE(string code)
— Это наш метод для парсинга строк
if (code.IndexOf ('#') != 0) {
— Если код не начинается на '#' (Это у нас комментарий) то парсим.
if(code.EndsWith(";")) {
— Продолжаем парсить если строка оканчивается на ';'
Дальше немного подробнее, ведь нам понадобится, хоть чуть-чуть, но логика.
Модель обрезания строки на функцию и аргументы:
function(arg1,arg2);
^ ^
a b
Мы вырезаем так:
string func = code.Substring(0,a);
— начинаем обрезание с 0 символа до первой '(', в итоге получаем function
string args = code.Substring(a+1,b-a-1);
— получаем аргументы начитая с первого символа идущего после '(' до ");" (+1)
string[] arg = args.Split(',');
— создаем массив с аргументами разделенные ','
func = func.Replace(" ", "");
— делаем это во избежание ошибки с пробелом.
switch(func.ToLower()) {
case "write":
funct.write(arg);
break;
case "writeline":
funct.writeline(arg);
break;
case "readline":
funct.readln(arg[0]);
break;
}
Вот Вам 3 базовые функции, write, writeline и readline.
Функции
У нас в языке есть 3 переменные,
public static string a;
public static char b;
public static int c;
с ними можно работать, это и есть новшество языка по сравненю с прошлой версей.
Код функций:
using System;
namespace lang
{
public class funct
{
public static string a;
public static char b;
public static int c;
public static void writeline (string[] args)
{
int z = 0;
string s = "";
if(args[z] == "a") { //Если writeline(a); то выводим переменную a
Console.WriteLine(a);
}
else if(args[z] == "b") { //Если writeline(b); то выводим переменную b
Console.WriteLine(b);
}
else if(args[z] == "c") { //Если writeline(c); то выводим переменную c
Console.WriteLine(c);
}
else {
while(z < args.Length) { //Выводим аргументы до тех пор пока не закончится массив
s += args[z] + " ";
z++;
}
s = s.Replace("%n", Environment.NewLine); //Заменяем %n на новую строку
s = s.Replace("%c", ","); //А %c на запятую
Console.WriteLine(s); //Выводим строку
}
}
public static void write (string[] args) //Аналогично writeline
{
int z = 0;
string s = "";
if(args[z] == "a") {
Console.WriteLine(a);
}
else if(args[z] == "b") {
Console.WriteLine(b);
}
else if(args[z] == "c") {
Console.WriteLine(c);
}
else {
while(z < args.Length) {
s += args[z] + " ";
z++;
}
s = s.Replace("%n", Environment.NewLine);
s = s.Replace("%c", ",");
Console.Write(s);
}
}
public static void readln (string args) {
if(args == "a") { //Если аргументы == a, то считываем переменную a
a = Console.ReadLine();
}
else if(args == "b") { //Упс, ошибка, не совместимость типов
Console.WriteLine("You can't use "b" variable as @STRING.");
}
else if(args == "c") { //Тоже самое
Console.WriteLine("You can't use "c" variable as @STRING.");
}
}
}
}
На этом первая часть заканчивается, вторая часть будет про файловую систему. До встречи!
Автор: EnderChiken