Create a very basic runtime/ns/scope with a basic repl

This commit is contained in:
Sameer Rahmani 2020-11-14 21:09:54 +00:00
parent c0fc5b152e
commit a7457fceb6
12 changed files with 396 additions and 12 deletions

38
bootstrap/cmd/repl.go Normal file
View File

@ -0,0 +1,38 @@
/*
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 cmd
import (
"github.com/spf13/cobra"
"serene-lang.org/bootstrap/pkg/core"
)
// replCmd represents the base command when called without any subcommands
var replCmd = &cobra.Command{
Use: "repl",
Short: "Runs the local Serene's REPL",
Long: `Runs the local Serene's REPL to interact with Serene`,
Run: func(cmd *cobra.Command, args []string) {
// TODO: Get the debug value from a CLI flag
core.REPL(false)
},
}
func init() {
rootCmd.AddCommand(replCmd)
}

View File

@ -22,12 +22,8 @@ import (
"os"
"github.com/spf13/cobra"
"serene-lang.org/bootstrap/pkg/parser"
"serene-lang.org/bootstrap/pkg/reader"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "Serene",
@ -43,9 +39,7 @@ to redistribute it under certain conditions;
for details take a look at the LICENSE file.
`,
Run: func(cmd *cobra.Command, args []string) {
reader.ReadString("sameer mary")
ast, _ := parser.ParseToAST("(asd 'mary '(1 2 3.4 -4 -0.3) `(asd ~asd ~@zxc))")
fmt.Printf("%s\n", ast.String())
fmt.Println("Fix me!!!! I don't do anything !!!")
},
}

View File

@ -3,6 +3,7 @@ module serene-lang.org/bootstrap
go 1.15
require (
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.1
)

View File

@ -26,6 +26,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=

View File

@ -0,0 +1,72 @@
/*
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 contains the high level internal function of Serene
package core
import (
"fmt"
"github.com/chzyer/readline"
"serene-lang.org/bootstrap/pkg/printer"
"serene-lang.org/bootstrap/pkg/reader"
"serene-lang.org/bootstrap/pkg/runtime"
)
func rep(rt *runtime.Runtime, line string) {
ast, err := reader.ReadString(line)
if err != nil {
fmt.Printf("Error: %s", err)
return
}
//eval.Eval(rt, ast)
printer.Print(rt, ast)
}
/** TODO:
Replace the readline implementation with go-prompt.
*/
func REPL(debug bool) {
rt := runtime.MakeRuntime(debug)
rt.CreateNS("user", "REPL", true)
rl, err := readline.New("> ")
if err != nil {
panic(err)
}
defer rl.Close()
fmt.Println(`Serene's bootstrap interpreter is used to
bootstrap the Serene's compiler.'
It comes with ABSOLUTELY NO WARRANTY;
This is free software, and you are welcome
to redistribute it under certain conditions;
for details take a look at the LICENSE file.
`)
for {
rl.SetPrompt(fmt.Sprintf("%s> ", rt.CurrentNS().GetName()))
line, err := rl.Readline()
if err != nil { // io.EOF
break
}
rep(rt, line)
}
}

View File

@ -0,0 +1,39 @@
/*
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 eval provides all the necessary functions to eval expressions
package eval
import (
"serene-lang.org/bootstrap/pkg/runtime"
"serene-lang.org/bootstrap/pkg/types"
)
func eval(rt *runtime.Runtime, forms types.ASTree) types.IExpr {
if len(forms) == 0 {
return &types.Nil
}
var ret types.IExpr
for _, form := range forms {
ret = eval_form(rt, rt.CurrentNS().GetRootScope(), form)
}
ret
}

View File

@ -0,0 +1,59 @@
/*
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 namespace provides an INamespace interface and one implementation
// of it to be used as the basic blocks of Serene's namespaces.
package namespace
import "serene-lang.org/bootstrap/pkg/scope"
type INamespace interface {
DefineGlobal()
LookupGlobal()
GetRootScope() scope.IScope
// return the fully qualified name of the namespace
GetName() string
}
type Namespace struct {
name string
rootScope scope.Scope
source string
externals map[string]Namespace
}
func (n *Namespace) DefineGlobal() {}
func (n *Namespace) LookupGlobal() {}
func (n *Namespace) GetRootScope() scope.IScope {
return &n.rootScope
}
func (n *Namespace) GetName() string {
return n.name
}
func MakeNS(name string, source string) Namespace {
return Namespace{
name: name,
rootScope: scope.MakeScope(nil),
source: source,
externals: map[string]Namespace{},
}
}

View File

@ -95,14 +95,15 @@ func readRawSymbol(parser IParsable) (types.IExpr, error) {
var symbol string
if c == nil {
return nil, errors.New("Unexpected EOF while parsing a symbol")
return nil, errors.New("unexpected EOF while parsing a symbol")
}
if isValidForSymbol(*c) {
parser.next(false)
symbol = *c
} else {
return nil, fmt.Errorf("Unexpected character: got '%s', expected a symbol at %s",
return nil, fmt.Errorf("unexpected character: got '%s', expected a symbol at %s",
*c,
parser.GetLocation(),
)
@ -143,7 +144,6 @@ func readNumber(parser IParsable, neg bool) (types.IExpr, error) {
}
if *c == "." && isDouble {
fmt.Println(result)
return nil, errors.New("a double with more that one '.' ???")
}
@ -163,7 +163,7 @@ func readNumber(parser IParsable, neg bool) (types.IExpr, error) {
break
}
}
fmt.Println(result)
return types.MakeNumberFromStr(result, isDouble)
}

View File

@ -0,0 +1,31 @@
/*
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 printer contains helper functions to printout AST and exprs
package printer
import (
"fmt"
"serene-lang.org/bootstrap/pkg/runtime"
"serene-lang.org/bootstrap/pkg/types"
)
func Print(rt *runtime.Runtime, ast types.ASTree) {
fmt.Println(ast.String())
}

View File

@ -15,7 +15,16 @@ 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 reader provides a set of functions to read forms from several
// different mediums
package reader
func ReadString(input string) {
import (
"serene-lang.org/bootstrap/pkg/parser"
"serene-lang.org/bootstrap/pkg/types"
)
func ReadString(input string) (types.ASTree, error) {
return parser.ParseToAST(input)
}

View File

@ -0,0 +1,73 @@
/*
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 runtime provides all the necessary functionality and data
// structures of Serene at runtime. You can think of the run time as
// Serene's state.
package runtime
import (
"fmt"
"serene-lang.org/bootstrap/pkg/namespace"
)
/** TODO:
Create an IRuntime interface to avoid using INamespace directly
*/
/** TODO:
Handle concurrency on the runtime level
*/
type Runtime struct {
namespaces map[string]namespace.Namespace
currentNS string
debugMode bool
}
func (r *Runtime) CurrentNS() *namespace.Namespace {
if r.currentNS == "" {
panic("current ns is not set on the runtime.")
}
ns, ok := r.namespaces[r.currentNS]
if !ok {
panic(fmt.Sprintf("namespace '%s' doesn't exist in the runtime.", r.currentNS))
}
return &ns
}
func (r *Runtime) CreateNS(name string, source string, setAsCurrent bool) {
ns := namespace.MakeNS(name, source)
if setAsCurrent {
r.currentNS = name
}
r.namespaces[name] = ns
}
func MakeRuntime(debug bool) *Runtime {
return &Runtime{
namespaces: map[string]namespace.Namespace{},
currentNS: "",
debugMode: debug,
}
}

View File

@ -0,0 +1,66 @@
/*
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 scope provides several interfaces and their implementations
// such `Scope` which acts as the environment in the Lisp literature.
package scope
import (
"fmt"
"serene-lang.org/bootstrap/pkg/types"
)
type IScope interface {
Lookup(k string) (*Binding, error)
Insert(k string, v types.IExpr, public bool)
}
type Binding struct {
value types.IExpr
public bool
}
type Scope struct {
bindings map[string]*Binding
parent IScope
}
func (s *Scope) Lookup(k string) (*Binding, error) {
v, ok := s.bindings[k]
if ok {
return v, nil
}
if s.parent != nil {
return s.parent.Lookup(k)
} else {
return nil, fmt.Errorf("can't resolve symbol '%s'", k)
}
}
func (s *Scope) Insert(k string, v types.IExpr, public bool) {
s.bindings[k] = &Binding{value: v, public: public}
}
func MakeScope(parent *Scope) Scope {
return Scope{
parent: parent,
bindings: map[string]*Binding{},
}
}