Add some documentation for the core package
This commit is contained in:
parent
468b446a4f
commit
a2725ba412
|
@ -1,4 +1,11 @@
|
||||||
* Serene lang
|
* Serene lang
|
||||||
|
|
||||||
|
** Development hint
|
||||||
|
|
||||||
|
*** Use Make... functions
|
||||||
|
In order to create a new value in any type use the designated Make function, for example: MakeList
|
||||||
|
|
||||||
|
|
||||||
** Setup development environment
|
** Setup development environment
|
||||||
*** Emacs
|
*** Emacs
|
||||||
[[https://github.com/brotzeit/rustic][Rustic]] is highly recommended. Just install it and install the dependencies necessary
|
[[https://github.com/brotzeit/rustic][Rustic]] is highly recommended. Just install it and install the dependencies necessary
|
||||||
|
|
|
@ -25,6 +25,10 @@ import (
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
"serene-lang.org/bootstrap/pkg/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Block struct represents a group of forms. Don't confuse it with
|
||||||
|
// code blocks from other languages that specify a block using curly
|
||||||
|
// brackets and indentation.
|
||||||
|
// Blocks in serene are just a group of forms and nothing more.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
body []IExpr
|
body []IExpr
|
||||||
}
|
}
|
||||||
|
@ -60,6 +64,7 @@ func (b *Block) SetContent(body []IExpr) {
|
||||||
b.body = body
|
b.body = body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append the given expr `form` to the block
|
||||||
func (b *Block) Append(form IExpr) {
|
func (b *Block) Append(form IExpr) {
|
||||||
b.body = append(b.body, form)
|
b.body = append(b.body, form)
|
||||||
}
|
}
|
||||||
|
@ -68,10 +73,13 @@ func (b *Block) Count() int {
|
||||||
return len(b.body)
|
return len(b.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeEmptyBlock creates an empty block
|
||||||
func MakeEmptyBlock() *Block {
|
func MakeEmptyBlock() *Block {
|
||||||
return &Block{}
|
return &Block{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeBlock creates a block that holds the given array of
|
||||||
|
// forms `body`.
|
||||||
func MakeBlock(body []IExpr) *Block {
|
func MakeBlock(body []IExpr) *Block {
|
||||||
return &Block{
|
return &Block{
|
||||||
body: body,
|
body: body,
|
||||||
|
|
|
@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
|
// ISeq is an interface describing a sequence of forms
|
||||||
type ISeq interface {
|
type ISeq interface {
|
||||||
First() IExpr
|
First() IExpr
|
||||||
Rest() ISeq
|
Rest() ISeq
|
||||||
|
@ -27,6 +28,7 @@ type ICountable interface {
|
||||||
Count() int
|
Count() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IColl describes a collection of values. A finite collection.
|
||||||
type IColl interface {
|
type IColl interface {
|
||||||
ISeq
|
ISeq
|
||||||
ICountable
|
ICountable
|
||||||
|
|
|
@ -34,6 +34,7 @@ func rep(rt *Runtime, line string) {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug data, ugly right ? :))
|
||||||
if rt.IsDebugMode() {
|
if rt.IsDebugMode() {
|
||||||
fmt.Println("\n### DEBUG ###")
|
fmt.Println("\n### DEBUG ###")
|
||||||
Print(rt, ast)
|
Print(rt, ast)
|
||||||
|
@ -53,6 +54,8 @@ func rep(rt *Runtime, line string) {
|
||||||
Replace the readline implementation with go-prompt.
|
Replace the readline implementation with go-prompt.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// REPL executes a Read Eval Print Loop locally reading from stdin and
|
||||||
|
// writing to stdout
|
||||||
func REPL(debug bool) {
|
func REPL(debug bool) {
|
||||||
rt := MakeRuntime(debug)
|
rt := MakeRuntime(debug)
|
||||||
rt.CreateNS("user", "REPL", true)
|
rt.CreateNS("user", "REPL", true)
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
Serene --- Yet an other Lisp
|
|
||||||
|
|
||||||
Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 2 of the License.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Def(rt *Runtime, scope IScope, args *List) (IExpr, error) {
|
|
||||||
switch args.Count() {
|
|
||||||
case 2:
|
|
||||||
name := args.First()
|
|
||||||
|
|
||||||
if name.GetType() != ast.Symbol {
|
|
||||||
return nil, errors.New("The first argument of 'def' has to be a symbol")
|
|
||||||
}
|
|
||||||
|
|
||||||
sym := name.(*Symbol)
|
|
||||||
|
|
||||||
//value = args.Rest().(*List).First()
|
|
||||||
valueExpr := args.Rest().First()
|
|
||||||
value, err := EvalForms(rt, scope, valueExpr)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ns := rt.CurrentNS()
|
|
||||||
ns.DefineGlobal(sym.GetName(), value, true)
|
|
||||||
return sym, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("'def' form need at least 2 arguments")
|
|
||||||
}
|
|
|
@ -25,7 +25,11 @@ import (
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
"serene-lang.org/bootstrap/pkg/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func EvalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) {
|
// evalForm evaluates the given expression `form` by a slightly different
|
||||||
|
// evaluation rules. For example if `form` is a list instead of the formal
|
||||||
|
// evaluation of a list it will evaluate all the elements and return the
|
||||||
|
// evaluated list
|
||||||
|
func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) {
|
||||||
|
|
||||||
switch form.GetType() {
|
switch form.GetType() {
|
||||||
case ast.Nil:
|
case ast.Nil:
|
||||||
|
@ -48,11 +52,7 @@ func EvalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) {
|
||||||
|
|
||||||
return expr.Value, nil
|
return expr.Value, nil
|
||||||
|
|
||||||
// List evaluation rules:
|
// Evaluate all the elements in the list instead of following the lisp convention
|
||||||
// * The first element of the list has to be an expression which implements `ICallable`
|
|
||||||
// * The rest o the elements have to be evaluated only after we have determind the the
|
|
||||||
// first element is `ICallable` and it's not a macro or special form.
|
|
||||||
// * An empty list evaluates to itself.
|
|
||||||
case ast.List:
|
case ast.List:
|
||||||
var result []IExpr
|
var result []IExpr
|
||||||
|
|
||||||
|
@ -78,14 +78,29 @@ func EvalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EvalForms evaluates the given expr `expressions` (it can be a list, block, symbol or anything else)
|
||||||
|
// with the given runtime `rt` and the scope `scope`.
|
||||||
func EvalForms(rt *Runtime, scope IScope, expressions IExpr) (IExpr, error) {
|
func EvalForms(rt *Runtime, scope IScope, expressions IExpr) (IExpr, error) {
|
||||||
|
// EvalForms is the main and the most important evaluation function on Serene.
|
||||||
|
// It's a long loooooooooooong function. Why? Well, Because we don't want to
|
||||||
|
// waste call stack spots in order to have a well organized code.
|
||||||
|
// In order to avoid stackoverflows and implement TCO ( which is a must for
|
||||||
|
// a functional language we need to avoid unnecessary calls and keep as much
|
||||||
|
// as possible in a loop.
|
||||||
var ret IExpr
|
var ret IExpr
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
tco:
|
tco:
|
||||||
for {
|
for {
|
||||||
|
// The TCO loop is there to take advantage or the fact that
|
||||||
|
// in order to call a function or a block we simply can change
|
||||||
|
// the value of the `expressions` and `scope`
|
||||||
var exprs []IExpr
|
var exprs []IExpr
|
||||||
|
|
||||||
|
// Block evaluation rules:
|
||||||
|
// * If empty, return Nothing
|
||||||
|
// * Otherwise evaluate the expressions in the block one by one
|
||||||
|
// and return the last result
|
||||||
if expressions.GetType() == ast.Block {
|
if expressions.GetType() == ast.Block {
|
||||||
if expressions.(*Block).Count() == 0 {
|
if expressions.(*Block).Count() == 0 {
|
||||||
return &Nothing, nil
|
return &Nothing, nil
|
||||||
|
@ -94,33 +109,43 @@ tco:
|
||||||
} else {
|
} else {
|
||||||
exprs = []IExpr{expressions}
|
exprs = []IExpr{expressions}
|
||||||
}
|
}
|
||||||
|
|
||||||
body:
|
body:
|
||||||
for _, forms := range exprs {
|
for _, forms := range exprs {
|
||||||
|
// Evaluating forms one by one
|
||||||
|
|
||||||
switch forms.GetType() {
|
if forms.GetType() != ast.List {
|
||||||
case ast.List:
|
ret, err = evalForm(rt, scope, forms)
|
||||||
|
break tco // return ret, err
|
||||||
default:
|
|
||||||
ret, err = EvalForm(rt, scope, forms)
|
|
||||||
break tco // return
|
|
||||||
}
|
|
||||||
|
|
||||||
if forms.(*List).Count() == 0 {
|
|
||||||
ret = &Nil
|
|
||||||
break tco // return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list := forms.(*List)
|
list := forms.(*List)
|
||||||
|
|
||||||
|
// Empty list evaluates to itself
|
||||||
|
if list.Count() == 0 {
|
||||||
|
ret = list
|
||||||
|
break tco // return &Nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
rawFirst := list.First()
|
rawFirst := list.First()
|
||||||
sform := ""
|
sform := ""
|
||||||
|
|
||||||
// Handling special forms
|
// Handling special forms by looking up the first
|
||||||
|
// element of the list. If it is a symbol, Grab
|
||||||
|
// the name and check it for build it forms.
|
||||||
|
//
|
||||||
|
// Note: If we don't care about recursion in any
|
||||||
|
// case we can simply extract it to a function
|
||||||
|
// for example in `def` since we are going to
|
||||||
|
// evaluate the value separately, we don't care
|
||||||
|
// about recursion because we're going to handle
|
||||||
|
// it wen we're evaluating the value. But in the
|
||||||
|
// case of let it's a different story.
|
||||||
if rawFirst.GetType() == ast.Symbol {
|
if rawFirst.GetType() == ast.Symbol {
|
||||||
sform = rawFirst.(*Symbol).GetName()
|
sform = rawFirst.(*Symbol).GetName()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch sform {
|
switch sform {
|
||||||
|
|
||||||
case "def":
|
case "def":
|
||||||
ret, err = Def(rt, scope, list.Rest().(*List))
|
ret, err = Def(rt, scope, list.Rest().(*List))
|
||||||
break tco // return
|
break tco // return
|
||||||
|
@ -128,8 +153,13 @@ tco:
|
||||||
case "fn":
|
case "fn":
|
||||||
ret, err = Fn(rt, scope, list.Rest().(*List))
|
ret, err = Fn(rt, scope, list.Rest().(*List))
|
||||||
break tco // return
|
break tco // return
|
||||||
|
|
||||||
|
// List evaluation rules:
|
||||||
|
// * The first element of the list has to be an expression which is callable
|
||||||
|
// * An empty list evaluates to itself.
|
||||||
default:
|
default:
|
||||||
exprs, e := EvalForm(rt, scope, list)
|
// Evaluating all the elements of the list
|
||||||
|
exprs, e := evalForm(rt, scope, list)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
err = e
|
err = e
|
||||||
ret = nil
|
ret = nil
|
||||||
|
@ -140,6 +170,11 @@ tco:
|
||||||
|
|
||||||
switch f.GetType() {
|
switch f.GetType() {
|
||||||
case ast.Fn:
|
case ast.Fn:
|
||||||
|
// If the first element of the evaluated list is a function
|
||||||
|
// create a scope for it by creating the binding to the given
|
||||||
|
// parameters in the new scope and set the parent of it to
|
||||||
|
// the scope which the function defined in and then set the
|
||||||
|
// `expressions` to the body of function and loop again
|
||||||
fn := f.(*Function)
|
fn := f.(*Function)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
err = e
|
err = e
|
||||||
|
@ -147,7 +182,7 @@ tco:
|
||||||
break body //return
|
break body //return
|
||||||
|
|
||||||
}
|
}
|
||||||
//argList, _ := args.(*List)
|
|
||||||
argList := exprs.(*List).Rest().(*List)
|
argList := exprs.(*List).Rest().(*List)
|
||||||
|
|
||||||
scope, e = MakeFnScope(fn.GetScope(), fn.GetParams(), argList)
|
scope, e = MakeFnScope(fn.GetScope(), fn.GetParams(), argList)
|
||||||
|
@ -171,8 +206,15 @@ tco:
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eval the given `Block` of code with the given runtime `rt`.
|
||||||
|
// The Important part here is that any expression that we need
|
||||||
|
// to Eval has to be wrapped in a Block. Don't confused the
|
||||||
|
// concept of Block with blocks from other languages which
|
||||||
|
// specify by using `{}` or indent or what ever. Blocks in terms
|
||||||
|
// of Serene are just arrays of expressions and nothing more.
|
||||||
func Eval(rt *Runtime, forms *Block) (IExpr, error) {
|
func Eval(rt *Runtime, forms *Block) (IExpr, error) {
|
||||||
if forms.Count() == 0 {
|
if forms.Count() == 0 {
|
||||||
|
// Nothing is literally Nothing
|
||||||
return &Nothing, nil
|
return &Nothing, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,16 +25,27 @@ import (
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
"serene-lang.org/bootstrap/pkg/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ICallable interface {
|
// Function struct represent a user defined function.
|
||||||
Apply(rt *Runtime, scope IScope, args *List) (IExpr, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Function struct {
|
type Function struct {
|
||||||
|
// Node struct holds the necessary functions to make
|
||||||
|
// Functions locatable
|
||||||
Node
|
Node
|
||||||
name string
|
|
||||||
scope IScope
|
// Name of the function, it can be empty and it has to be
|
||||||
|
// set via `def`
|
||||||
|
name string
|
||||||
|
|
||||||
|
// Parent scope of the function. The scope which the function
|
||||||
|
// is defined in
|
||||||
|
scope IScope
|
||||||
|
|
||||||
|
// A collection of arguments. Why IColl? because we can use
|
||||||
|
// Lists and Vectors for the argument lists. Maybe even
|
||||||
|
// hashmaps in future.
|
||||||
params IColl
|
params IColl
|
||||||
body *Block
|
|
||||||
|
// A reference to the body block of the function
|
||||||
|
body *Block
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Function) GetType() ast.NodeType {
|
func (f *Function) GetType() ast.NodeType {
|
||||||
|
@ -66,6 +77,8 @@ func (f *Function) GetBody() *Block {
|
||||||
return f.body
|
return f.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeFunction Create a function with the given `params` and `body` in
|
||||||
|
// the given `scope`.
|
||||||
func MakeFunction(scope IScope, params IColl, body *Block) *Function {
|
func MakeFunction(scope IScope, params IColl, body *Block) *Function {
|
||||||
return &Function{
|
return &Function{
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
@ -74,11 +87,14 @@ func MakeFunction(scope IScope, params IColl, body *Block) *Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeFnScope a new scope for the body of a function. It binds the `bindings`
|
||||||
|
// to the given `values`.
|
||||||
func MakeFnScope(parent IScope, bindings IColl, values IColl) (*Scope, error) {
|
func MakeFnScope(parent IScope, bindings IColl, values IColl) (*Scope, error) {
|
||||||
fmt.Printf("%s %s\n", bindings, values)
|
fmt.Printf("%s %s\n", bindings, values)
|
||||||
scope := MakeScope(parent.(*Scope))
|
scope := MakeScope(parent.(*Scope))
|
||||||
|
|
||||||
// TODO: Implement destructuring
|
// TODO: Implement destructuring
|
||||||
|
|
||||||
if bindings.Count() > values.Count() {
|
if bindings.Count() > values.Count() {
|
||||||
return nil, errors.New("'binding' and 'valuse' size don't match")
|
return nil, errors.New("'binding' and 'valuse' size don't match")
|
||||||
}
|
}
|
||||||
|
@ -87,6 +103,10 @@ func MakeFnScope(parent IScope, bindings IColl, values IColl) (*Scope, error) {
|
||||||
exprs := values.ToSlice()
|
exprs := values.ToSlice()
|
||||||
|
|
||||||
for i := 0; i < len(binds); i += 1 {
|
for i := 0; i < len(binds); i += 1 {
|
||||||
|
// If an argument started with char `&` use it to represent
|
||||||
|
// rest of values.
|
||||||
|
//
|
||||||
|
// for example: `(fn (x y &z) ...)`
|
||||||
if binds[i].GetType() == ast.Symbol && binds[i].(*Symbol).IsRestable() {
|
if binds[i].GetType() == ast.Symbol && binds[i].(*Symbol).IsRestable() {
|
||||||
scope.Insert(binds[i+1].(*Symbol).GetName(), MakeList(exprs[i:]), false)
|
scope.Insert(binds[i+1].(*Symbol).GetName(), MakeList(exprs[i:]), false)
|
||||||
break
|
break
|
||||||
|
|
|
@ -22,6 +22,7 @@ import "serene-lang.org/bootstrap/pkg/ast"
|
||||||
|
|
||||||
type NilType struct{}
|
type NilType struct{}
|
||||||
|
|
||||||
|
// Nil is just Nil not `null` or anything
|
||||||
var Nil = NilType{}
|
var Nil = NilType{}
|
||||||
|
|
||||||
func (n NilType) GetType() ast.NodeType {
|
func (n NilType) GetType() ast.NodeType {
|
||||||
|
|
|
@ -40,6 +40,8 @@ type StringParser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing IParsable for StringParser ---
|
// Implementing IParsable for StringParser ---
|
||||||
|
|
||||||
|
// Returns the next character in the buffer
|
||||||
func (sp *StringParser) next(skipWhitespace bool) *string {
|
func (sp *StringParser) next(skipWhitespace bool) *string {
|
||||||
if sp.pos >= len(sp.buffer) {
|
if sp.pos >= len(sp.buffer) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -54,6 +56,7 @@ func (sp *StringParser) next(skipWhitespace bool) *string {
|
||||||
return &char
|
return &char
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the character of the buffer without consuming it
|
||||||
func (sp *StringParser) peek(skipWhitespace bool) *string {
|
func (sp *StringParser) peek(skipWhitespace bool) *string {
|
||||||
if sp.pos >= len(sp.buffer) {
|
if sp.pos >= len(sp.buffer) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -67,6 +70,7 @@ func (sp *StringParser) peek(skipWhitespace bool) *string {
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move the char pointer back by one character
|
||||||
func (sp *StringParser) back() {
|
func (sp *StringParser) back() {
|
||||||
if sp.pos > 0 {
|
if sp.pos > 0 {
|
||||||
sp.pos = sp.pos - 1
|
sp.pos = sp.pos - 1
|
||||||
|
@ -78,6 +82,7 @@ func (sp *StringParser) GetLocation() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// END: IParsable ---
|
// END: IParsable ---
|
||||||
|
|
||||||
func contains(s []rune, c rune) bool {
|
func contains(s []rune, c rune) bool {
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
if v == c {
|
if v == c {
|
||||||
|
|
|
@ -24,6 +24,40 @@ import (
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
"serene-lang.org/bootstrap/pkg/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Def defines a global binding in the current namespace. The first
|
||||||
|
// arguments in `args` has to be a symbol ( none ns qualified ) and
|
||||||
|
// the second param should be the value of the binding
|
||||||
|
func Def(rt *Runtime, scope IScope, args *List) (IExpr, error) {
|
||||||
|
|
||||||
|
// TODO: Add support for docstrings and meta
|
||||||
|
|
||||||
|
switch args.Count() {
|
||||||
|
case 2:
|
||||||
|
name := args.First()
|
||||||
|
|
||||||
|
if name.GetType() != ast.Symbol {
|
||||||
|
return nil, errors.New("the first argument of 'def' has to be a symbol")
|
||||||
|
}
|
||||||
|
|
||||||
|
sym := name.(*Symbol)
|
||||||
|
|
||||||
|
valueExpr := args.Rest().First()
|
||||||
|
value, err := EvalForms(rt, scope, valueExpr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := rt.CurrentNS()
|
||||||
|
ns.DefineGlobal(sym.GetName(), value, true)
|
||||||
|
return sym, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("'def' form need at least 2 arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fn defines a function inside the given scope `scope` with the given `args`.
|
||||||
|
// `args` contains the arugment list, docstring and body of the function.
|
||||||
func Fn(rt *Runtime, scope IScope, args *List) (IExpr, error) {
|
func Fn(rt *Runtime, scope IScope, args *List) (IExpr, error) {
|
||||||
|
|
||||||
if args.Count() < 1 {
|
if args.Count() < 1 {
|
||||||
|
@ -46,5 +80,4 @@ func Fn(rt *Runtime, scope IScope, args *List) (IExpr, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return MakeFunction(scope, params, body), nil
|
return MakeFunction(scope, params, body), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package types provides the type interface of Serene. All the types
|
|
||||||
// in Serene are directly AST Nodes as well.
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -26,14 +24,22 @@ import (
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
"serene-lang.org/bootstrap/pkg/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IPrintable is the interface which any value that wants to have a string
|
||||||
|
// representation has to implement. The `print` family functions will use
|
||||||
|
// this interface to convert forms to string
|
||||||
type IPrintable interface {
|
type IPrintable interface {
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDebuggable is the interface designed for converting forms to a string
|
||||||
|
// form which are meant to be used as debug data
|
||||||
type IDebuggable interface {
|
type IDebuggable interface {
|
||||||
ToDebugStr() string
|
ToDebugStr() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IExpr is the most important interface in Serene which basically represents
|
||||||
|
// a VALUE in Serene. All the forms (beside special formss) has to implement
|
||||||
|
// this interface.
|
||||||
type IExpr interface {
|
type IExpr interface {
|
||||||
ast.ILocatable
|
ast.ILocatable
|
||||||
ast.ITypable
|
ast.ITypable
|
||||||
|
@ -41,10 +47,13 @@ type IExpr interface {
|
||||||
IDebuggable
|
IDebuggable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node struct is simply representing a Node in the AST which provides the
|
||||||
|
// functionalities required to trace the code based on the location.
|
||||||
type Node struct {
|
type Node struct {
|
||||||
location int
|
location int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLocation returns the location of the Node in the source input
|
||||||
func (n Node) GetLocation() int {
|
func (n Node) GetLocation() int {
|
||||||
return n.location
|
return n.location
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue