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);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
using AST = Finn.AST;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Net;
|
||||
|
||||
namespace Finn;
|
||||
|
||||
@ -23,7 +26,7 @@ public class Env
|
||||
|
||||
public Env()
|
||||
{
|
||||
this.enclosing = null;
|
||||
enclosing = null;
|
||||
}
|
||||
|
||||
public Env(Env enclosing)
|
||||
@ -31,17 +34,18 @@ public class Env
|
||||
this.enclosing = enclosing;
|
||||
}
|
||||
|
||||
public object this[Token identifier]
|
||||
public object this[string name]
|
||||
{
|
||||
set
|
||||
{
|
||||
var name = (string)identifier.Literal!;
|
||||
if (values.ContainsKey(name))
|
||||
{
|
||||
throw new RuntimeError(identifier, $"Cannot redefine variable {name} in same scope.");
|
||||
}
|
||||
// Redefinition within the same scope should never happen, but front
|
||||
// end should catch it.
|
||||
values[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[Token identifier]
|
||||
{
|
||||
get
|
||||
{
|
||||
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>
|
||||
{
|
||||
private class PatternMismatchException : Exception
|
||||
@ -115,7 +142,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
{
|
||||
return ValueTuple.Create();
|
||||
}
|
||||
env[pattern.Identifier] = obj;
|
||||
env[(string)pattern.Identifier.Literal!] = obj;
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = evaluate(new Env(), expression);
|
||||
var value = evaluate(globals, expression);
|
||||
Console.WriteLine(value);
|
||||
}
|
||||
catch (RuntimeError err)
|
||||
@ -387,7 +424,19 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
|
||||
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)
|
||||
|
18
Parser.cs
18
Parser.cs
@ -335,6 +335,15 @@ class Parser
|
||||
|
||||
if (match(TokenType.LParen))
|
||||
{
|
||||
return finishCall(expr);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Expr finishCall(Expr callee)
|
||||
{
|
||||
var lParen = previous();
|
||||
var args = new List<Expr?>();
|
||||
while (!check(TokenType.RParen))
|
||||
{
|
||||
@ -352,10 +361,13 @@ class Parser
|
||||
}
|
||||
}
|
||||
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()
|
||||
|
6
TODO.txt
6
TODO.txt
@ -1,2 +1,8 @@
|
||||
Implement Finn functions
|
||||
Figure out multiple-binding let-expr semantics
|
||||
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 Type = { Name: string; Fields: list<Field>; Start: string }
|
||||
|
||||
type Type =
|
||||
{ Name: string
|
||||
Fields: list<Field>
|
||||
Start: string }
|
||||
|
||||
let exprTypes: Type list =
|
||||
[ { Name = "Sequence"
|
||||
@ -12,13 +16,14 @@ let exprTypes: Type list =
|
||||
{ Type = "Expr"; Name = "Right" } ]
|
||||
Start = "Left.Start" }
|
||||
{ Name = "Grouping"
|
||||
Fields = [ { Type = "Expr"; Name = "Expression" } ;{Type="Token";Name="LParen"}]
|
||||
Start = "LParen"}
|
||||
Fields = [ { Type = "Expr"; Name = "Expression" }; { Type = "Token"; Name = "LParen" } ]
|
||||
Start = "LParen" }
|
||||
{ Name = "Literal"
|
||||
Fields =
|
||||
[ { Type = "System.Object"
|
||||
Name = "Value" }; {Type="Token"; Name="Token"} ]
|
||||
Start = "Token"}
|
||||
Name = "Value" }
|
||||
{ Type = "Token"; Name = "Token" } ]
|
||||
Start = "Token" }
|
||||
{ Name = "Unary"
|
||||
Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ]
|
||||
Start = "Op" }
|
||||
@ -33,18 +38,23 @@ let exprTypes: Type list =
|
||||
Fields = [ { Type = "Token"; Name = "Value" } ]
|
||||
Start = "Value" }
|
||||
{ Name = "List"
|
||||
Fields = [ { Type = "Expr[]"; Name = "Elements" }; {Type="Token"; Name = "LBracket"} ]
|
||||
Fields =
|
||||
[ { Type = "Expr[]"; Name = "Elements" }
|
||||
{ Type = "Token"; Name = "LBracket" } ]
|
||||
Start = "LBracket" }
|
||||
{ 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" }
|
||||
{ Name = "Record"
|
||||
Fields =
|
||||
[ { Type = "Field[]"
|
||||
Name = "Extensions" }
|
||||
{ Type = "BaseRecord?"; Name = "Base" }
|
||||
{ Type = "Token"; Name= "LBrace"} ]
|
||||
Start = "LBrace"}
|
||||
{ Type = "Token"; Name = "LBrace" } ]
|
||||
Start = "LBrace" }
|
||||
{ Name = "Selector"
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Token"; Name = "FieldName" } ]
|
||||
Start = "Left.Start" }
|
||||
@ -52,29 +62,35 @@ let exprTypes: Type list =
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ]
|
||||
Start = "Left.Start" }
|
||||
{ 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" }
|
||||
{ Name = "Let"
|
||||
Fields =
|
||||
[ { Type = "Binding[]"
|
||||
Name = "Bindings" }
|
||||
{ Type = "Expr"; Name = "Body" }
|
||||
{ Type = "Token"; Name = "LetToken"} ]
|
||||
{ Type = "Token"; Name = "LetToken" } ]
|
||||
Start = "LetToken" }
|
||||
{ Name = "When"
|
||||
Fields =
|
||||
[ { Type = "Expr"; Name = "Head" }
|
||||
{ Type = "VarBinding[]"
|
||||
Name = "Cases" }
|
||||
{ Type = "Token"; Name = "WhenToken"} ]
|
||||
{ Type = "Token"; Name = "WhenToken" } ]
|
||||
Start = "WhenToken" } ]
|
||||
|
||||
let patternTypes =
|
||||
[ { Name = "SimplePattern"
|
||||
Fields = [ { Type = "Token?"; Name = "Identifier" }; {Type = "Token"; Name = "Token"}]
|
||||
Fields = [ { Type = "Token?"; Name = "Identifier" }; { Type = "Token"; Name = "Token" } ]
|
||||
Start = "Token" }
|
||||
{ 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" }
|
||||
{ Name = "FieldPattern"
|
||||
Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ]
|
||||
@ -85,8 +101,7 @@ let patternTypes =
|
||||
Name = "Fields" }
|
||||
{ Type = "SimplePattern?"
|
||||
Name = "Rest" }
|
||||
{ Type = "Token"
|
||||
Name = "LBrace"} ]
|
||||
{ Type = "Token"; Name = "LBrace" } ]
|
||||
Start = "LBrace" } ]
|
||||
|
||||
let visitorMethod baseName t = $"visit{t.Name}{baseName}"
|
||||
|
Loading…
Reference in New Issue
Block a user