v2: Implement Interpreter.atg with typed FunctionValue, mixed-arg functions, and expression-binding (:=); fix Coco/R csproj command
This completes the v2 grammar based on master’s capabilities while aligning with the v2 design philosophy (numeric-first, strings as function-args only): - Implement full arithmetic grammar ( +, -, *, /, %, ^, parentheses, unary sign ), with support for number/hex/binary literals. - Add variables and arrays: - Numeric variables via :: (evaluate RHS now and store numeric) - Expression-bound variables via := (capture RHS text; re-parse and evaluate at use-time) - Numeric arrays via -> and array literals + indexing (with bounds/type checks) - Add comparisons (==, <, >, <=, >=) producing FunctionValue TRUE/FALSE. - Add function calls with mixed arguments: numeric expressions and quoted string literals. - In numeric contexts, enforce numeric results; emit clear type errors if a function returns a string. - Root production always produces a FunctionValue through InputInterpreter.ProduceOutput(FunctionValue). Runtime integration (expected APIs on InputInterpreter): - Variable APIs: AssignNumeric, AssignExpression, AssignNumericArray; TryGetNumeric, TryGetExpression, TryGetNumericArray - Function dispatch: ExecuteFunction(name, FunctionArgument[]) - Expression evaluation for :=: EvaluateExpression(expressionText) Coco/R build fix: - Correct the PreBuild command in src/core/core.csproj: - Use -frames (without the stray space) and point to cocor - Use the correct case and path for the grammar: cocor/Interpreter.atg Notes: - Strings are valid only as function arguments and not as standalone values or variables. - Grammar emits concise, actionable error messages for type mismatches, missing variables, and array bounds.
This commit is contained in:
parent
66ec23a420
commit
f532d13d8c
2 changed files with 307 additions and 85 deletions
|
@ -1,49 +1,35 @@
|
|||
using csmic;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using csmic;
|
||||
|
||||
COMPILER INTERPRETER
|
||||
|
||||
/*
|
||||
*
|
||||
* Class Structures
|
||||
*
|
||||
* Class structures and helpers
|
||||
*/
|
||||
|
||||
private FunctionValue value = new FunctionValue();
|
||||
private FunctionValue functionValue = new FunctionValue();
|
||||
|
||||
public decimal CalculatedValue
|
||||
public FunctionValue Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
get { return this.functionValue; }
|
||||
set { this.functionValue = value; }
|
||||
}
|
||||
|
||||
private InputInterpreter interpreter = null;
|
||||
|
||||
public InputInterpreter Interpreter
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.interpreter;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.interpreter = value;
|
||||
}
|
||||
get { return this.interpreter; }
|
||||
set { this.interpreter = value; }
|
||||
}
|
||||
|
||||
bool IsFunctionCall()
|
||||
{
|
||||
scanner.ResetPeek();
|
||||
Token next = scanner.Peek();
|
||||
if (next.kind == _LPAREN && la.kind == _identifier)
|
||||
return true;
|
||||
if (next.kind == _LPAREN && la.kind == _identifier) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -51,8 +37,7 @@ bool IsCompare()
|
|||
{
|
||||
scanner.ResetPeek();
|
||||
Token next = scanner.Peek();
|
||||
if (next.kind == _COMPARER)
|
||||
return true;
|
||||
if (next.kind == _COMPARER) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -60,8 +45,7 @@ bool IsAssignment()
|
|||
{
|
||||
scanner.ResetPeek();
|
||||
Token next = scanner.Peek();
|
||||
if (next.val == "::" || next.val == ":=" || next.val == "->")
|
||||
return true;
|
||||
if (next.val == "::" || next.val == ":=" || next.val == "->") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -69,14 +53,12 @@ bool IsArrayCall()
|
|||
{
|
||||
scanner.ResetPeek();
|
||||
Token next = scanner.Peek();
|
||||
if(next.val == "[")
|
||||
return true;
|
||||
if (next.val == "[") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parser definitions
|
||||
*
|
||||
* Character sets and tokens
|
||||
*/
|
||||
|
||||
CHARACTERS
|
||||
|
@ -106,24 +88,264 @@ TOKENS
|
|||
IGNORE cr + tab
|
||||
|
||||
/*
|
||||
* Parser specification
|
||||
*
|
||||
* Grammar
|
||||
*/
|
||||
|
||||
PRODUCTIONS
|
||||
|
||||
INTERPRETER (.
|
||||
FunctionValue functionValue = new FunctionValue();
|
||||
FunctionValue fv = new FunctionValue();
|
||||
bool success = true;
|
||||
if(this.interpreter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this.interpreter == null) { return; }
|
||||
.)
|
||||
=
|
||||
|
||||
IF(IsCompare())
|
||||
Comparison<out success> (. this.functionValue = (success == true) ? FunctionValue.TRUE : FunctionValue.FALSE; .)
|
||||
Comparison<out success>
|
||||
(. this.functionValue = (success == true) ? FunctionValue.TRUE : FunctionValue.FALSE;
|
||||
this.interpreter.ProduceOutput(this.functionValue);
|
||||
.)
|
||||
|
|
||||
IF(IsAssignment())
|
||||
Assignment<out r>
|
||||
(. this.functionValue = new FunctionValue(ValueType.Numeric, r);
|
||||
this.interpreter.ProduceOutput(this.functionValue);
|
||||
.)
|
||||
|
|
||||
Expression<out r>
|
||||
(. this.functionValue = new FunctionValue(ValueType.Numeric, r);
|
||||
this.interpreter.ProduceOutput(this.functionValue);
|
||||
.)
|
||||
.
|
||||
|
||||
Expression<out decimal r>
|
||||
=
|
||||
(. decimal r1 = 0; r = 0; .)
|
||||
Term<out r>
|
||||
{ '+' Term<out r1> (. r += r1; .)
|
||||
| '-' Term<out r1> (. r -= r1; .)
|
||||
}
|
||||
.
|
||||
|
||||
Term<out decimal r>
|
||||
=
|
||||
(. decimal r1 = 0; r = 0; .)
|
||||
Factor<out r>
|
||||
{ '*' Factor<out r1> (. r *= r1; .)
|
||||
| '/' Factor<out r1> (. r /= r1; .)
|
||||
| '%' Term<out r1> (. r %= r1; .)
|
||||
}
|
||||
.
|
||||
|
||||
Factor<out decimal r>
|
||||
=
|
||||
(. decimal r1 = 0; .)
|
||||
Value<out r>
|
||||
{ '^' Expression<out r1>
|
||||
(. r = Convert.ToDecimal(Math.Pow(Convert.ToDouble(r), Convert.ToDouble(r1))); .)
|
||||
}
|
||||
.
|
||||
|
||||
Value<out decimal r> (.
|
||||
r = 0; decimal r1 = 0; int signum = 1;
|
||||
FunctionValue fvr = new FunctionValue();
|
||||
string ident = string.Empty;
|
||||
.)
|
||||
=
|
||||
[ '+' | '-' (. signum = -1; .) ]
|
||||
(
|
||||
IF(IsFunctionCall())
|
||||
Function<out fvr>
|
||||
(.
|
||||
if (fvr.Type == ValueType.Numeric && fvr.Value != null)
|
||||
{
|
||||
try { r = signum * Convert.ToDecimal(fvr.Value); }
|
||||
catch { SemErr("function returned non-numeric"); r = 0; }
|
||||
}
|
||||
else
|
||||
{
|
||||
SemErr("function returned a string; number required");
|
||||
r = 0;
|
||||
}
|
||||
.)
|
||||
|
|
||||
IF(IsArrayCall())
|
||||
ArrayCall<out r> (. r = signum * r; .)
|
||||
|
|
||||
identifier
|
||||
(.
|
||||
ident = t.val;
|
||||
decimal temp = 0;
|
||||
string expr = string.Empty;
|
||||
bool ok = false;
|
||||
// Prefer numeric binding
|
||||
try
|
||||
{
|
||||
// runtime method expected
|
||||
ok = this.interpreter.TryGetNumeric(ident, out temp);
|
||||
}
|
||||
catch { ok = false; }
|
||||
if (ok)
|
||||
{
|
||||
r = signum * temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check expression binding
|
||||
try
|
||||
{
|
||||
if (this.interpreter.TryGetExpression(ident, out expr))
|
||||
{
|
||||
FunctionValue eval = this.interpreter.EvaluateExpression(expr);
|
||||
if (eval.Type == ValueType.Numeric && eval.Value != null)
|
||||
{
|
||||
r = signum * Convert.ToDecimal(eval.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
SemErr("expression variable did not evaluate to a number");
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SemErr("variable '" + ident + "' is not numeric");
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
catch { SemErr("error evaluating expression variable"); r = 0; }
|
||||
}
|
||||
.)
|
||||
|
|
||||
number (. r = signum * Convert.ToDecimal(t.val); .)
|
||||
|
|
||||
hex (. string hx = t.val.Remove(0,2);
|
||||
try { r = signum * Convert.ToDecimal(Convert.ToInt64(hx, 16)); }
|
||||
catch { r = 0; }
|
||||
.)
|
||||
|
|
||||
binary (. string bx = t.val.Remove(t.val.Length - 1);
|
||||
try { r = signum * Convert.ToDecimal(Convert.ToInt64(bx, 2)); }
|
||||
catch { r = 0; }
|
||||
.)
|
||||
|
|
||||
'(' Expression<out r> ')'
|
||||
(. r = signum * r; .)
|
||||
)
|
||||
.
|
||||
|
||||
ArrayL<out decimal[] d>
|
||||
=
|
||||
(. List<decimal> list = new List<decimal>(); decimal r = 0; d = new decimal[0]; .)
|
||||
'['
|
||||
Expression<out r> (. list.Add(r); d = list.ToArray(); .)
|
||||
{ ',' Expression<out r> (. list.Add(r); d = list.ToArray(); .) }
|
||||
']'
|
||||
.
|
||||
|
||||
ArrayCall<out decimal r> (. string ident = string.Empty; r = 0; decimal pos = 0; .)
|
||||
=
|
||||
identifier (. ident = t.val; .)
|
||||
'['
|
||||
Expression<out pos>
|
||||
(.
|
||||
try
|
||||
{
|
||||
int i = Convert.ToInt32(pos);
|
||||
decimal[] values;
|
||||
if (this.interpreter.TryGetNumericArray(ident, out values))
|
||||
{
|
||||
if (i >= 0 && i < values.Length) { r = values[i]; }
|
||||
else { SemErr("array index out of range"); r = 0; }
|
||||
}
|
||||
else
|
||||
{
|
||||
SemErr("variable '" + ident + "' is not a numeric array");
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
catch { SemErr("invalid array index"); r = 0; }
|
||||
.)
|
||||
']'
|
||||
.
|
||||
|
||||
Assignment<out decimal r>
|
||||
(. string identifier = string.Empty; string expression = string.Empty; decimal[] d = new decimal[0]; r = 0; .)
|
||||
=
|
||||
identifier (. identifier = t.val; .)
|
||||
(
|
||||
(
|
||||
"::"
|
||||
Expression<out r>
|
||||
(. this.interpreter.AssignNumeric(identifier, r); .)
|
||||
)
|
||||
|
|
||||
(
|
||||
":="
|
||||
AnyExpression<out expression>
|
||||
(. this.interpreter.AssignExpression(identifier, expression); r = 0; .)
|
||||
)
|
||||
|
|
||||
(
|
||||
"->"
|
||||
ArrayL<out d>
|
||||
(. this.interpreter.AssignNumericArray(identifier, d); r = 0; .)
|
||||
)
|
||||
)
|
||||
.
|
||||
|
||||
// Function call with mixed arguments (numeric expressions and strings)
|
||||
Function<out FunctionValue r>
|
||||
(. string functionName = string.Empty; FunctionArgument[] args = new FunctionArgument[0]; r = new FunctionValue(); .)
|
||||
=
|
||||
identifier (. functionName = t.val; .)
|
||||
'('
|
||||
ArgList<out args>
|
||||
')'
|
||||
(. r = this.interpreter.ExecuteFunction(functionName, args); .)
|
||||
.
|
||||
|
||||
ArgList<out FunctionArgument[] args>
|
||||
(. List<FunctionArgument> list = new List<FunctionArgument>(); FunctionArgument a = new FunctionArgument { Name = string.Empty, Value = new FunctionValue() }; args = new FunctionArgument[0]; .)
|
||||
=
|
||||
[
|
||||
Arg<out a> (. list.Add(a); args = list.ToArray(); .)
|
||||
{ ',' Arg<out a> (. list.Add(a); args = list.ToArray(); .) }
|
||||
]
|
||||
.
|
||||
|
||||
Arg<out FunctionArgument arg>
|
||||
(. arg = new FunctionArgument { Name = string.Empty, Value = new FunctionValue() }; decimal r = 0; string s = string.Empty; .)
|
||||
=
|
||||
(
|
||||
string (. s = t.val.Substring(1, t.val.Length - 2); arg = new FunctionArgument { Name = string.Empty, Value = new FunctionValue(ValueType.String, s) }; .)
|
||||
|
|
||||
Expression<out r> (. arg = new FunctionArgument { Name = string.Empty, Value = new FunctionValue(ValueType.Numeric, r) }; .)
|
||||
)
|
||||
.
|
||||
|
||||
Comparison<out bool result>
|
||||
(. decimal firstValue = 0; decimal secondValue = 0; string compareType = string.Empty; result = false; .)
|
||||
=
|
||||
Expression<out firstValue>
|
||||
COMPARER (. compareType = t.val; .)
|
||||
Expression<out secondValue>
|
||||
(.
|
||||
switch(compareType)
|
||||
{
|
||||
case "==": result = (firstValue == secondValue); break;
|
||||
case ">": result = (firstValue > secondValue); break;
|
||||
case "<": result = (firstValue < secondValue); break;
|
||||
case ">=": result = (firstValue >= secondValue); break;
|
||||
case "<=": result = (firstValue <= secondValue); break;
|
||||
default: result = false; break;
|
||||
}
|
||||
.)
|
||||
.
|
||||
|
||||
AnyExpression<out string value>
|
||||
(. value = string.Empty; StringBuilder builder = new StringBuilder(); .)
|
||||
=
|
||||
ANY (. builder.Append(t.val); .) { ANY (. builder.Append(t.val); .) } (. value = builder.ToString(); .)
|
||||
.
|
||||
|
||||
END INTERPRETER.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="dotnet tool run coco -namespace csmic.Interpreter - frames . $(ProjectDir)\cocor\interpreter.atg" />
|
||||
<Exec Command="dotnet tool run coco -namespace csmic.Interpreter -frames $(ProjectDir)cocor $(ProjectDir)cocor/Interpreter.atg" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue