Implemented native function calls
This commit is contained in:
parent
43fcb615fb
commit
ab38d85465
@ -111,7 +111,7 @@ public partial record Indexer(Expr Left, Expr Index) : Expr(Left.Start)
|
|||||||
return visitor.visitIndexerExpr(context, this);
|
return visitor.visitIndexerExpr(context, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public partial record Call(Expr Left, Expr?[] Arguments) : Expr(Left.Start)
|
public partial record Call(Expr Left, Expr?[] Arguments, Token LParen) : Expr(Left.Start)
|
||||||
{
|
{
|
||||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
using AST = Finn.AST;
|
using AST = Finn.AST;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Finn;
|
namespace Finn;
|
||||||
|
|
||||||
@ -23,7 +26,7 @@ public class Env
|
|||||||
|
|
||||||
public Env()
|
public Env()
|
||||||
{
|
{
|
||||||
this.enclosing = null;
|
enclosing = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Env(Env enclosing)
|
public Env(Env enclosing)
|
||||||
@ -31,17 +34,18 @@ public class Env
|
|||||||
this.enclosing = enclosing;
|
this.enclosing = enclosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object this[Token identifier]
|
public object this[string name]
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
var name = (string)identifier.Literal!;
|
// Redefinition within the same scope should never happen, but front
|
||||||
if (values.ContainsKey(name))
|
// end should catch it.
|
||||||
{
|
|
||||||
throw new RuntimeError(identifier, $"Cannot redefine variable {name} in same scope.");
|
|
||||||
}
|
|
||||||
values[name] = value;
|
values[name] = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object this[Token identifier]
|
||||||
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var name = (string)identifier.Literal!;
|
var name = (string)identifier.Literal!;
|
||||||
@ -61,6 +65,29 @@ public class Env
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface Callable
|
||||||
|
{
|
||||||
|
object Call(Interpreter interpreter, object[] arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record NativeFunction(Func<object[], object> Function) : Callable
|
||||||
|
{
|
||||||
|
public object Call(Interpreter interpreter, object[] arguments)
|
||||||
|
{
|
||||||
|
return this.Function(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => "<native func>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserFunction : Callable
|
||||||
|
{
|
||||||
|
public object Call(Interpreter interpreter, object[] arguments)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Interpreter : AST.IExprVisitor<Env, object>
|
public class Interpreter : AST.IExprVisitor<Env, object>
|
||||||
{
|
{
|
||||||
private class PatternMismatchException : Exception
|
private class PatternMismatchException : Exception
|
||||||
@ -115,7 +142,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
|||||||
{
|
{
|
||||||
return ValueTuple.Create();
|
return ValueTuple.Create();
|
||||||
}
|
}
|
||||||
env[pattern.Identifier] = obj;
|
env[(string)pattern.Identifier.Literal!] = obj;
|
||||||
return ValueTuple.Create();
|
return ValueTuple.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,11 +299,21 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Env globals = new Env();
|
||||||
|
|
||||||
|
public Interpreter()
|
||||||
|
{
|
||||||
|
globals["clock"] = new NativeFunction((args) =>
|
||||||
|
{
|
||||||
|
return (double)DateTimeOffset.Now.ToUnixTimeSeconds();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void Interpret(AST.Expr expression)
|
public void Interpret(AST.Expr expression)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var value = evaluate(new Env(), expression);
|
var value = evaluate(globals, expression);
|
||||||
Console.WriteLine(value);
|
Console.WriteLine(value);
|
||||||
}
|
}
|
||||||
catch (RuntimeError err)
|
catch (RuntimeError err)
|
||||||
@ -387,7 +424,19 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
|||||||
|
|
||||||
public object visitCallExpr(Env env, AST.Call expr)
|
public object visitCallExpr(Env env, AST.Call expr)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
var callee = evaluate(env, expr.Left);
|
||||||
|
object[] args = new object[expr.Arguments.Length];
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
var argExpr = expr.Arguments[i];
|
||||||
|
if (argExpr is null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("partial application not implemented");
|
||||||
|
}
|
||||||
|
args[i] = evaluate(env, argExpr);
|
||||||
|
}
|
||||||
|
Callable function = (Callable)callee;
|
||||||
|
return function.Call(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object visitGroupingExpr(Env env, AST.Grouping expr)
|
public object visitGroupingExpr(Env env, AST.Grouping expr)
|
||||||
|
18
Parser.cs
18
Parser.cs
@ -335,6 +335,15 @@ class Parser
|
|||||||
|
|
||||||
if (match(TokenType.LParen))
|
if (match(TokenType.LParen))
|
||||||
{
|
{
|
||||||
|
return finishCall(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expr finishCall(Expr callee)
|
||||||
|
{
|
||||||
|
var lParen = previous();
|
||||||
var args = new List<Expr?>();
|
var args = new List<Expr?>();
|
||||||
while (!check(TokenType.RParen))
|
while (!check(TokenType.RParen))
|
||||||
{
|
{
|
||||||
@ -352,10 +361,13 @@ class Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
consume("Expect ')' after arguments.", TokenType.RParen);
|
consume("Expect ')' after arguments.", TokenType.RParen);
|
||||||
return new Call(expr, args.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr;
|
const int MaxArgs = 25;
|
||||||
|
if (args.Count > MaxArgs)
|
||||||
|
{
|
||||||
|
error(peek(), $"Can't have more than {MaxArgs} arguments.");
|
||||||
|
}
|
||||||
|
return new Call(callee, args.ToArray(), lParen);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr operand()
|
private Expr operand()
|
||||||
|
6
TODO.txt
6
TODO.txt
@ -1,2 +1,8 @@
|
|||||||
|
Implement Finn functions
|
||||||
Figure out multiple-binding let-expr semantics
|
Figure out multiple-binding let-expr semantics
|
||||||
Inject error handling into parser and scanner
|
Inject error handling into parser and scanner
|
||||||
|
Think about a way to hide record fields
|
||||||
|
- Maybe a compile-time primitive for generating globally unique symbols?
|
||||||
|
Would also need a syntax to use those symbols.
|
||||||
|
- Use @ as an operator to convert a comptime-known string to an identifier
|
||||||
|
Replace `with` keyword with `but`
|
@ -1,5 +1,9 @@
|
|||||||
type Field = { Type: string; Name: string }
|
type Field = { Type: string; Name: string }
|
||||||
type Type = { Name: string; Fields: list<Field>; Start: string }
|
|
||||||
|
type Type =
|
||||||
|
{ Name: string
|
||||||
|
Fields: list<Field>
|
||||||
|
Start: string }
|
||||||
|
|
||||||
let exprTypes: Type list =
|
let exprTypes: Type list =
|
||||||
[ { Name = "Sequence"
|
[ { Name = "Sequence"
|
||||||
@ -17,7 +21,8 @@ let exprTypes: Type list =
|
|||||||
{ Name = "Literal"
|
{ Name = "Literal"
|
||||||
Fields =
|
Fields =
|
||||||
[ { Type = "System.Object"
|
[ { Type = "System.Object"
|
||||||
Name = "Value" }; {Type="Token"; Name="Token"} ]
|
Name = "Value" }
|
||||||
|
{ Type = "Token"; Name = "Token" } ]
|
||||||
Start = "Token" }
|
Start = "Token" }
|
||||||
{ Name = "Unary"
|
{ Name = "Unary"
|
||||||
Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ]
|
Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ]
|
||||||
@ -33,10 +38,15 @@ let exprTypes: Type list =
|
|||||||
Fields = [ { Type = "Token"; Name = "Value" } ]
|
Fields = [ { Type = "Token"; Name = "Value" } ]
|
||||||
Start = "Value" }
|
Start = "Value" }
|
||||||
{ Name = "List"
|
{ Name = "List"
|
||||||
Fields = [ { Type = "Expr[]"; Name = "Elements" }; {Type="Token"; Name = "LBracket"} ]
|
Fields =
|
||||||
|
[ { Type = "Expr[]"; Name = "Elements" }
|
||||||
|
{ Type = "Token"; Name = "LBracket" } ]
|
||||||
Start = "LBracket" }
|
Start = "LBracket" }
|
||||||
{ Name = "Variant"
|
{ Name = "Variant"
|
||||||
Fields = [ { Type = "Token"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" }; {Type="Token";Name="Backtick"}]
|
Fields =
|
||||||
|
[ { Type = "Token"; Name = "Tag" }
|
||||||
|
{ Type = "Expr?"; Name = "Argument" }
|
||||||
|
{ Type = "Token"; Name = "Backtick" } ]
|
||||||
Start = "Backtick" }
|
Start = "Backtick" }
|
||||||
{ Name = "Record"
|
{ Name = "Record"
|
||||||
Fields =
|
Fields =
|
||||||
@ -52,7 +62,10 @@ let exprTypes: Type list =
|
|||||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ]
|
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ]
|
||||||
Start = "Left.Start" }
|
Start = "Left.Start" }
|
||||||
{ Name = "Call"
|
{ Name = "Call"
|
||||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr?[]"; Name = "Arguments" } ]
|
Fields =
|
||||||
|
[ { Type = "Expr"; Name = "Left" }
|
||||||
|
{ Type = "Expr?[]"; Name = "Arguments" }
|
||||||
|
{ Type = "Token"; Name = "LParen" } ]
|
||||||
Start = "Left.Start" }
|
Start = "Left.Start" }
|
||||||
{ Name = "Let"
|
{ Name = "Let"
|
||||||
Fields =
|
Fields =
|
||||||
@ -74,7 +87,10 @@ let patternTypes =
|
|||||||
Fields = [ { Type = "Token?"; Name = "Identifier" }; { Type = "Token"; Name = "Token" } ]
|
Fields = [ { Type = "Token?"; Name = "Identifier" }; { Type = "Token"; Name = "Token" } ]
|
||||||
Start = "Token" }
|
Start = "Token" }
|
||||||
{ Name = "VariantPattern"
|
{ Name = "VariantPattern"
|
||||||
Fields = [ { Type = "Token"; Name = "Tag" }; { Type = "Pattern?"; Name = "Argument" }; {Type="Token"; Name = "Backtick"} ]
|
Fields =
|
||||||
|
[ { Type = "Token"; Name = "Tag" }
|
||||||
|
{ Type = "Pattern?"; Name = "Argument" }
|
||||||
|
{ Type = "Token"; Name = "Backtick" } ]
|
||||||
Start = "Backtick" }
|
Start = "Backtick" }
|
||||||
{ Name = "FieldPattern"
|
{ Name = "FieldPattern"
|
||||||
Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ]
|
Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ]
|
||||||
@ -85,8 +101,7 @@ let patternTypes =
|
|||||||
Name = "Fields" }
|
Name = "Fields" }
|
||||||
{ Type = "SimplePattern?"
|
{ Type = "SimplePattern?"
|
||||||
Name = "Rest" }
|
Name = "Rest" }
|
||||||
{ Type = "Token"
|
{ Type = "Token"; Name = "LBrace" } ]
|
||||||
Name = "LBrace"} ]
|
|
||||||
Start = "LBrace" } ]
|
Start = "LBrace" } ]
|
||||||
|
|
||||||
let visitorMethod baseName t = $"visit{t.Name}{baseName}"
|
let visitorMethod baseName t = $"visit{t.Name}{baseName}"
|
||||||
|
Loading…
Reference in New Issue
Block a user