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)
|
||||||
|
48
Parser.cs
48
Parser.cs
@ -335,29 +335,41 @@ class Parser
|
|||||||
|
|
||||||
if (match(TokenType.LParen))
|
if (match(TokenType.LParen))
|
||||||
{
|
{
|
||||||
var args = new List<Expr?>();
|
return finishCall(expr);
|
||||||
while (!check(TokenType.RParen))
|
|
||||||
{
|
|
||||||
if (match(TokenType.Blank))
|
|
||||||
{
|
|
||||||
args.Add(null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
args.Add(expression());
|
|
||||||
}
|
|
||||||
if (!match(TokenType.Comma))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
consume("Expect ')' after arguments.", TokenType.RParen);
|
|
||||||
return new Call(expr, args.ToArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Expr finishCall(Expr callee)
|
||||||
|
{
|
||||||
|
var lParen = previous();
|
||||||
|
var args = new List<Expr?>();
|
||||||
|
while (!check(TokenType.RParen))
|
||||||
|
{
|
||||||
|
if (match(TokenType.Blank))
|
||||||
|
{
|
||||||
|
args.Add(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
args.Add(expression());
|
||||||
|
}
|
||||||
|
if (!match(TokenType.Comma))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consume("Expect ')' after arguments.", TokenType.RParen);
|
||||||
|
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
if (match(TokenType.Number, TokenType.String))
|
if (match(TokenType.Number, TokenType.String))
|
||||||
|
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"
|
||||||
@ -12,13 +16,14 @@ let exprTypes: Type list =
|
|||||||
{ Type = "Expr"; Name = "Right" } ]
|
{ Type = "Expr"; Name = "Right" } ]
|
||||||
Start = "Left.Start" }
|
Start = "Left.Start" }
|
||||||
{ Name = "Grouping"
|
{ Name = "Grouping"
|
||||||
Fields = [ { Type = "Expr"; Name = "Expression" } ;{Type="Token";Name="LParen"}]
|
Fields = [ { Type = "Expr"; Name = "Expression" }; { Type = "Token"; Name = "LParen" } ]
|
||||||
Start = "LParen"}
|
Start = "LParen" }
|
||||||
{ Name = "Literal"
|
{ Name = "Literal"
|
||||||
Fields =
|
Fields =
|
||||||
[ { Type = "System.Object"
|
[ { Type = "System.Object"
|
||||||
Name = "Value" }; {Type="Token"; Name="Token"} ]
|
Name = "Value" }
|
||||||
Start = "Token"}
|
{ Type = "Token"; Name = "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" } ]
|
||||||
Start = "Op" }
|
Start = "Op" }
|
||||||
@ -33,18 +38,23 @@ 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 =
|
||||||
[ { Type = "Field[]"
|
[ { Type = "Field[]"
|
||||||
Name = "Extensions" }
|
Name = "Extensions" }
|
||||||
{ Type = "BaseRecord?"; Name = "Base" }
|
{ Type = "BaseRecord?"; Name = "Base" }
|
||||||
{ Type = "Token"; Name= "LBrace"} ]
|
{ Type = "Token"; Name = "LBrace" } ]
|
||||||
Start = "LBrace"}
|
Start = "LBrace" }
|
||||||
{ Name = "Selector"
|
{ Name = "Selector"
|
||||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Token"; Name = "FieldName" } ]
|
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Token"; Name = "FieldName" } ]
|
||||||
Start = "Left.Start" }
|
Start = "Left.Start" }
|
||||||
@ -52,29 +62,35 @@ 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 =
|
||||||
[ { Type = "Binding[]"
|
[ { Type = "Binding[]"
|
||||||
Name = "Bindings" }
|
Name = "Bindings" }
|
||||||
{ Type = "Expr"; Name = "Body" }
|
{ Type = "Expr"; Name = "Body" }
|
||||||
{ Type = "Token"; Name = "LetToken"} ]
|
{ Type = "Token"; Name = "LetToken" } ]
|
||||||
Start = "LetToken" }
|
Start = "LetToken" }
|
||||||
{ Name = "When"
|
{ Name = "When"
|
||||||
Fields =
|
Fields =
|
||||||
[ { Type = "Expr"; Name = "Head" }
|
[ { Type = "Expr"; Name = "Head" }
|
||||||
{ Type = "VarBinding[]"
|
{ Type = "VarBinding[]"
|
||||||
Name = "Cases" }
|
Name = "Cases" }
|
||||||
{ Type = "Token"; Name = "WhenToken"} ]
|
{ Type = "Token"; Name = "WhenToken" } ]
|
||||||
Start = "WhenToken" } ]
|
Start = "WhenToken" } ]
|
||||||
|
|
||||||
let patternTypes =
|
let patternTypes =
|
||||||
[ { Name = "SimplePattern"
|
[ { Name = "SimplePattern"
|
||||||
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