I have added, thanks to MGrammar, a few new constructs to my fledgling Calculatix language:
- unary operators, used primarily so that I can specify negative whole numbers
- support for string literals
- the slopeof expression, allowing for the calculation of the slope of a line
- the show statement, allowing for the output of an expression’s value to the console
This allows me to write input such as this:
line LineA (4, -3) (2, 5);
show "The slope of LineA is:";
show slopeof LineA;
The MGrammar for this language now looks like this:
1: module JeffFerguson.Calculatix.Grammars
2: {
3: language Calculus
4: {
5:
6: // ignore whitespace
7:
8: syntax LF = "\u000A";
9: syntax CR = "\u000D";
10: syntax Space = "\u0020";
11: interleave Whitespace = LF | CR | Space;
12:
13: // punctuation
14: token Comma = ',';
15:
16: // operators
17: token UnaryOperator = "+" | "-";
18: token Quote = "\"";
19:
20: // keywords
21: token LineKeyword = "line";
22: token ShowKeyword = "show";
23: token SlopeKeyword = "slopeof";
24:
25: // numbers
26: token Digit = "0".."9";
27: token WholeNumber = UnaryOperator? Digit+;
28:
29:
30: // strings
31: token StringLiteral = Quote a:any* Quote => StringLiteral[a];
32:
33: // identifiers
34: token Uppercase = "A".."Z";
35: token Lowercase = "a".."z";
36: token Alphabetic = Uppercase | Lowercase;
37: token Identifier = Alphabetic (Alphabetic | Digit)*;
38:
39: // expressions
40: syntax SlopeExpression = SlopeKeyword i:Identifier => Slope[i];
41: syntax Expression =
42: SlopeExpression
43: | StringLiteral
44: ;
45:
46: // points
47: token XCoordinate = WholeNumber;
48: token YCoordinate = WholeNumber;
49: syntax Point = "(" x:XCoordinate "," y:YCoordinate ")" =>Point[x,y];
50:
51: // lines
52: syntax LineStatement = LineKeyword i:Identifier p1:Point p2:Point => Line[i, p1, p2];
53: syntax SlopeStatement = SlopeKeyword i:Identifier => Slope[i];
54:
55: // expression display
56: syntax ShowStatement = ShowKeyword e:Expression => Show[valuesof(e)];
57:
58: // statements
59: syntax StatementExpression =
60: LineStatement
61: | ShowStatement;
62:
63: // language syntax
64: syntax Statement = e:StatementExpression ";" =>Statement[valuesof(e)];
65: syntax Main = Statement*;
66: }
67: }
This input shown above produces a graph that looks like this:
Main[
[
Statement[
Line[
"LineA",
Point[
"4",
"-3"
],
Point[
"2",
"5"
]
]
],
Statement[
Show[
StringLiteral[
"The slope of LineA is:"
]
]
],
Statement[
Show[
Slope[
"LineA"
]
]
]
]
]
It is worth noting that I have used two terms here - expression and statement – and it is probably important to discuss the difference (at least according to how I have defined them). A statement is an action. When a statement is encountered, an action is taken: perhaps an object is created in the .NET code that processes the source input, or information is displayed to the user. When an expression is encountered, an evaluation is made: perhaps a calculation is performed, or some memory is reserved for a value. Expressions do nothing, however, until used in a statement. The current grammar defines two types expressions:
- string literals
- slope expressions via the slopeof keyword
Since the show statement is defined as accepting an expression, either expression type can be used in the show statement, and, as you can see, the source input uses both of those types.
It’s also important to note what the grammar doesn’t do:
- Identifier Validation: The intent of the slopeof expression is that the identifier used in the expression was previously used to define a line. The grammar doesn’t check that, nor can it, nor should it. That has to be checked by the .NET code that is processing the input. This is an example of semantics versus syntax. MGrammar will handle the syntax, but you need to handle the semantics. In this example, the code that checks to ensure that the identifier used in the slopeof expression actually belongs to a line will be code that I have to write in the .NET processor.
- Calculation: The grammar doesn’t calculate the actual slope of the line. Again, that’s code that must be left to the .NET processor.
Next up – processing this input in C#. With any luck, I will be able to show the slope’s calculation on the console.