Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function with constants variables #2

Open
mrxrsd opened this issue Jun 7, 2019 · 14 comments
Open

Function with constants variables #2

mrxrsd opened this issue Jun 7, 2019 · 14 comments
Labels

Comments

@mrxrsd
Copy link

mrxrsd commented Jun 7, 2019

Hi,

Is it possible to add constant vars when build a function? something like this:

var fn = calc.ParseFunction("a+b+c");
fn.Variables.Add("a",1);
fn.Variables.Add("b",1);
var compileFn = fn.Compile();
var result = compileFn(new Dictionary<string, double> { { "c", 2 } });

Or I need to interpolation constants variable before parse function?
tks

@yallie
Copy link
Owner

yallie commented Jun 8, 2019

Hello @mrxrsd, here you are:

using ParameterList = System.Collections.Generic.Dictionary<string, double>;

public Expression<Func<ParameterList, double>> ParseFunction(string text,
	ParameterList constants)
{
	var calc = new XtensibleCalculator();
	var parsed = calc.ParseFunction(text).Compile();
	return c => CallWithConstants(parsed, constants, c);
}

public double CallWithConstants(Func<ParameterList, double> function,
	ParameterList constants, ParameterList variables)
{
	var allVars = constants.Concat(variables).ToDictionary(p => p.Key, p => p.Value);
	return function(allVars);
}

You can use the code like this:

using ParameterList = System.Collections.Generic.Dictionary<string, double>;

static void Main()
{
	// parse function with constants
	var constants = new ParameterList{{ "a", 1 }, { "b", 2 }};
	var parsed = ParseFunction("a+b+c", constants);
	var compiled = parsed.Compile();

	// call compiled function with variables
	var variables = new ParameterList{{ "c", 3 }};
	compiled(variables);
}

@yallie yallie added the question label Jun 8, 2019
@jzabroski
Copy link

It seems like it would be nice to have a more succinct way to write this, so that you can go from a simple calculator to a Von Neumann style computer with registers / saved state. The following doesn't seem possible in Sprache.Calc out of the box:

A = 1
B = 2
def Mul(a, b, c) = a * b * c
Mul(A, B, 3)

Otherwise, this library seems like a good start for a project I want to prove to myself I can write.

To be clear, I think some guidelines on the following might be worth brainstorming:

  • Assignables (Immutable variables , lefthand values whose value is the result of a righthand expression)
  • variadic arguments
  • bonus: currying

@thiagottt
Copy link

It seems like it would be nice to have a more succinct way to write this, so that you can go from a simple calculator to a Von Neumann style computer with registers / saved state. The following doesn't seem possible in Sprache.Calc out of the box:

A = 1
B = 2
def Mul(a, b, c) = a * b * c
Mul(A, B, 3)

Otherwise, this library seems like a good start for a project I want to prove to myself I can write.

To be clear, I think some guidelines on the following might be worth brainstorming:

  • Assignables (Immutable variables , lefthand values whose value is the result of a righthand expression)
  • variadic arguments
  • bonus: currying

Hi,

Take a look at Jace .net.

pieterderycke/Jace#43

@jzabroski
Copy link

@thiagottt Interesting; thanks. I started making progress towards using Sprache. Do you have any comparison between the two? It seems as though Jace.NET doesn't actually have a "local constant registry" as proposed in that issue, nor is it extensible like Sprache or Sprache.Calc is.

For some background, I am trying to replace a homebrewed alert system written using regular expressions. The language examples looks something like:

currentVal = someExcelFunction / someOtherExcelFunction
previousVal = 
currentVal/previousVal-1

or

currentVal = someExcelFunction / someOtherExcelFunction
previousVal = 
temp=currentVal/previousVal-1
=temp>.01

or

today = today()
updateDate = 41399
needUpdate = today > updateDate

currentVal = someExcelFunction / someOtherExcelFunction
previousVal = 
temp=currentVal/previousVal-1
needAlert = temp< 1

or(needAlert, needUpdate)

Effectively, the last line in a "program" is always the return value. It may or may not start with an "=" sign.

@jzabroski
Copy link

I think that, I can't use Jace.NET, given the constraints of over >1000 tiny "programs" I would then need to port to Jace.NET.

@yallie
Copy link
Owner

yallie commented Oct 17, 2019

It seems like it would be nice to have a more succinct way to write this, so that you can go from a simple calculator to a Von Neumann style computer with registers / saved state. The following doesn't seem possible in Sprache.Calc out of the box...

Keep in mind that Sprache.Calc doesn't have extensible AST. It uses LINQ expressions as the data model, so everything it generates should be assignable to Expression.

As far as I remember, LINQ expressions have support for statements since .NET 4.x, something like Expression.Block, Expression.IfThen, etc. So in theory we could build something like a tiny imperative language based on Sprache.Calc's approach. Not sure about local functions though :)

@jzabroski
Copy link

Hi @yallie
I made decent progress prototyping a small language. Your project is a good template for thinking about things.

One piece of feedback on your CodeProject article: It is a little counter-intuitive to me to declare the Parser properties as virtual instead of public static, especially reading your article on CodeProject. I think the one thing you didn't explain well was that XtensibleCalculator will derive from ScientificCalculator will derive from SimpleCalculator. If you read the code from the bottom of SimpleCalculator upwards, reading through ScientificCalculator and XtensibleCalculator is a lot easier. In fact, I found ScientificCalculator is mostly irrelevant for learning how to use Sprache.

@jzabroski
Copy link

jzabroski commented Oct 17, 2019

Keep in mind that Sprache.Calc doesn't have extensible AST. It uses LINQ expressions as the data model, so everything it generates should be assignable to Expression.

Just thinking about this comment.

Why can't you "lift" such expressions?

In effect, massage the data model a bit before trying to convert everything to an expression tree. You can use erecruit.Expr to simplify some of your expression tree manipualtion.

@yallie
Copy link
Owner

yallie commented Oct 17, 2019

Thanks for your feedback!

Sprache.Calc is my study on grammar inheritance, and everything is virtual so it can be overridden in derived classes. It's not a tutorial on writing Sprache parsers, it's more like an experiment on alternative ways of combining parsers.

Using built-in LINQ expression classes is just a way to keep my own code down to minimum. I definitely agree that custom AST classes would give much more flexibility in terms of manipulating the expressions, but it wasn't the goal :)

@jzabroski
Copy link

Yeah, you may want to Google for The Expression Problem / The Extensibility Problem.

Rather than phrasing the problem as "experiment on ways combining parsers", rephrase the problem as independently adding new operators and operands.

Note: I am good at the theory, but suck at the practice, so easier said than done:)

@yallie
Copy link
Owner

yallie commented Oct 17, 2019

Rather than phrasing the problem as "experiment on ways combining parsers", rephrase the problem as independently adding new operators and operands.

I was interested in parsing different language dialects, like PostgreSQL and Oracle SQL for example, while reusing the core parser. Using OOP inheritance seemed to me like a natural tool for overriding different language parts.

@jzabroski
Copy link

jzabroski commented Oct 17, 2019 via email

@yallie
Copy link
Owner

yallie commented Oct 17, 2019

It appears Nicholas has written a "sequel" to Sprache called Superpower

Yes, it's not as easy to use as Sprache, but it probably has better error reporting.

Did you ever write C#-based parsers for those languages?

No I didn't, that was just an example, sorry :)
Not sure if I ever used grammar inheritance in a real project.

@jzabroski
Copy link

jzabroski commented Oct 17, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants