Skip to content

Instantly share code, notes, and snippets.

@yallie
Created October 11, 2020 16:12
Show Gist options
  • Save yallie/4025016ef0a640f39d21072ae1e38de8 to your computer and use it in GitHub Desktop.
Save yallie/4025016ef0a640f39d21072ae1e38de8 to your computer and use it in GitHub Desktop.
Tiny Pascal parser example using Sprache.Calc
using System;
using System.Linq;
using System.Linq.Expressions;
using Mono.Linq.Expressions;
using Sprache;
using Sprache.Calc;
namespace Pascal
{
class Program
{
static void Main()
{
var parser = new PascalParser();
var expr = parser.ParseProgram(@"
begin
a := 1 + 2;
if a > 10 then a := 10;
if a < 20 then
a := a + 1
else
a := a - 1;
if a > 20 then
begin
a := 30;
a := a + b
end
else
begin
a := b - a
end
end.
");
Console.WriteLine(CSharp.ToCSharpCode(expr));
}
}
public class PascalParser : XtensibleCalculator
{
public Expression ParseProgram(string text) =>
Program.Parse(text);
protected internal virtual Parser<Expression> Program =>
from b in Block.Token()
from s in Parse.Char('.').Token()
select b;
// block => begin statements end
protected internal virtual Parser<Expression> Block =>
from b in Parse.String("begin").Token()
from s in Statements.Token()
from e in Parse.String("end").Token()
select s;
// delimiter => ";"
protected internal virtual Parser<Expression> Delimiter =>
Parse.Char(';').Token().Return(default(Expression));
// statements => statement { ";" statement... }
protected internal virtual Parser<Expression> Statements =>
from st in Statement.Token().DelimitedBy(Delimiter)
select Expression.Block(st.Where(s => s != null));
// statement => null | begin ... end | assignment
protected internal virtual Parser<Expression> Statement =>
Parse.String("null").Token().Return(default(Expression))
.XOr(Block)
.XOr(IfStatement)
.XOr(Assignment)
.XOr(Delimiter);
protected override Parser<Expression> Parameter =>
from id in Identifier
from n in Parse.Not(Parse.Char('('))
select Expression.Variable(typeof(double), id);
protected internal virtual Parser<ExpressionType> GreaterThan =>
Operator(">", ExpressionType.GreaterThan);
protected internal virtual Parser<ExpressionType> LessThan =>
Operator("<", ExpressionType.LessThan);
protected internal virtual Parser<Expression> BoolExpr =>
Parse.ChainOperator(GreaterThan.Or(LessThan), Expr, Expression.MakeBinary);
protected internal virtual Parser<Expression> IfStatement =>
from i in Parse.String("if").Token()
from test in BoolExpr.Token()
from t in Parse.String("then").Token()
from @then in Statement
from @else in ElseStatement.Token().Optional()
select @else.IsDefined ?
Expression.IfThenElse(test, @then, @else.GetOrDefault()) :
Expression.IfThen(test, @then);
protected internal virtual Parser<Expression> ElseStatement =>
from e in Parse.String("else").Token()
from st in Statement
select st;
protected internal virtual Parser<Expression> Assignment =>
from v in Identifier.Token()
from a in Parse.String(":=").Token()
from x in Expr.Token()
select Expression.Assign(Expression.Variable(typeof(double), v), x);
}
}
@yallie
Copy link
Author

yallie commented Oct 11, 2020

Project initialization:

>md Pascal
>cd Pascal
>dotnet new console
>dotnet add package Sprache.Calc
>dotnet add package Mono.Linq.Expressions

Copy and paste pascal.cs contents to the Program.cs file, then run;

>dotnet run

The code will dump C# representation of the Pascal program, something like that:

{
	a = checked { 1 + 2 };
	if (a > 10)
	{
		return a = 10;
	}
	else
	{
		default(void);
	}
	if (a < 20)
	{
		return a = checked { a + 1 };
	}
	else
	{
		return a = checked { a - 1 };
	}
	if (a > 20)
	{
		a = 30;
		return a = checked { a + b };
	}
	else
	{
		return a = checked { b - a };
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment