Add support for printable strings and 'pr', 'prn' functionalities.
Add 'pr' and 'prn' functions to print out the representation of an string and 'print' and 'println' to print out the string.
This commit is contained in:
parent
fb6c1b3ba3
commit
699483e249
|
@ -1,3 +1,9 @@
|
|||
(ns examples.hello-world)
|
||||
|
||||
(def hello-world (fn () 'helloworld))
|
||||
(def hello-world
|
||||
(fn ()
|
||||
"helloworld"))
|
||||
|
||||
(def main
|
||||
(fn (&args)
|
||||
(println "sameer")))
|
||||
|
|
|
@ -18,27 +18,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BUILTINS is used in the Runtime to support builtin functions of
|
||||
// the language which are implemented in Go
|
||||
var BUILTINS = map[string]NativeFunction{
|
||||
"pr": MakeNativeFn("pr", PrNativeFn),
|
||||
"prn": MakeNativeFn("pr", PrnNativeFn),
|
||||
"print": MakeNativeFn("print", PrintNativeFn),
|
||||
"println": MakeNativeFn("println", PrintlnNativeFn),
|
||||
"require": MakeNativeFn("print", RequireNativeFn),
|
||||
"hash": MakeNativeFn("hash", HashNativeFn),
|
||||
}
|
||||
|
||||
func PrNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
Pr(rt, toRepresentables(args.Rest().(IColl))...)
|
||||
return &Nil, nil
|
||||
}
|
||||
|
||||
func PrnNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
|
||||
Prn(rt, toRepresentables(args.Rest().(IColl))...)
|
||||
return &Nil, nil
|
||||
}
|
||||
|
||||
func PrintNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
var params []string
|
||||
Print(rt, toRepresentables(args.Rest().(IColl))...)
|
||||
return &Nil, nil
|
||||
}
|
||||
|
||||
for _, expr := range args.Rest().(*List).ToSlice() {
|
||||
params = append(params, expr.String())
|
||||
}
|
||||
|
||||
fmt.Print(strings.Join(params, " "))
|
||||
func PrintlnNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
Println(rt, toRepresentables(args.Rest().(IColl))...)
|
||||
return &Nil, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ type IError interface {
|
|||
ast.ILocatable
|
||||
|
||||
// We want errors to be printable by the `print` family
|
||||
IPrintable
|
||||
IRepresentable
|
||||
IDebuggable
|
||||
|
||||
// To wrap Golan rrrrors
|
||||
|
|
|
@ -36,6 +36,9 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
case ast.Number:
|
||||
return form, nil
|
||||
|
||||
case ast.Fn:
|
||||
return form, nil
|
||||
|
||||
case ast.String:
|
||||
return form, nil
|
||||
|
||||
|
@ -117,10 +120,9 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
}
|
||||
return MakeList(result), nil
|
||||
}
|
||||
|
||||
panic("Asd")
|
||||
// Default case
|
||||
return nil, MakeError(rt, "not implemented")
|
||||
|
||||
return nil, MakeError(rt, fmt.Sprintf("support for '%d' is not implemented", form.GetType()))
|
||||
}
|
||||
|
||||
// EvalForms evaluates the given expr `expressions` (it can be a list, block, symbol or anything else)
|
||||
|
@ -294,7 +296,7 @@ tco:
|
|||
// the symbol name binded to the value
|
||||
case "def":
|
||||
ret, err = Def(rt, scope, list.Rest().(*List))
|
||||
break tco // return
|
||||
continue body
|
||||
|
||||
// `defmacro` evaluation rules:
|
||||
// * The first argument has to be a symbol
|
||||
|
@ -303,7 +305,7 @@ tco:
|
|||
// body of the macro.
|
||||
case "defmacro":
|
||||
ret, err = DefMacro(rt, scope, list.Rest().(*List))
|
||||
break tco // return
|
||||
continue body
|
||||
|
||||
// `macroexpand` evaluation rules:
|
||||
// * It has to have only one argument
|
||||
|
@ -327,7 +329,7 @@ tco:
|
|||
// * Defines an anonymous function.
|
||||
case "fn":
|
||||
ret, err = Fn(rt, scope, list.Rest().(*List))
|
||||
break tco // return
|
||||
continue body
|
||||
|
||||
// `if` evaluation rules:
|
||||
// * It has to get only 3 arguments: PRED THEN ELSE
|
||||
|
|
|
@ -109,6 +109,10 @@ func (f *Function) GetName() string {
|
|||
return f.name
|
||||
}
|
||||
|
||||
func (f *Function) SetName(name string) {
|
||||
f.name = name
|
||||
}
|
||||
|
||||
func (f *Function) GetScope() IScope {
|
||||
return f.scope
|
||||
}
|
||||
|
@ -129,6 +133,11 @@ func (f *Function) GetBody() *Block {
|
|||
return f.body
|
||||
}
|
||||
|
||||
func (f *Function) Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
application := args.Cons(f)
|
||||
return EvalForms(rt, scope, application)
|
||||
}
|
||||
|
||||
// MakeFunction Create a function with the given `params` and `body` in
|
||||
// the given `scope`.
|
||||
func MakeFunction(scope IScope, params IColl, body *Block) *Function {
|
||||
|
@ -144,15 +153,24 @@ func MakeFunction(scope IScope, params IColl, body *Block) *Function {
|
|||
// to the given `values`.
|
||||
func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Scope, IError) {
|
||||
scope := MakeScope(parent.(*Scope))
|
||||
|
||||
// TODO: Implement destructuring
|
||||
|
||||
if bindings.Count() > values.Count() {
|
||||
return nil, MakeError(rt, "'binding' and 'valuse' size don't match")
|
||||
}
|
||||
|
||||
binds := bindings.ToSlice()
|
||||
exprs := values.ToSlice()
|
||||
numberOfBindings := len(binds)
|
||||
lastBinding := binds[len(binds)-1]
|
||||
|
||||
if lastBinding.GetType() == ast.Symbol && lastBinding.(*Symbol).IsRestable() {
|
||||
numberOfBindings = len(binds) - 1
|
||||
}
|
||||
|
||||
if numberOfBindings > len(exprs) {
|
||||
if rt.IsDebugMode() {
|
||||
fmt.Printf("[DEBUG] Mismatch on bindings and values: Bindings: %s, Values: %s\n", bindings, values)
|
||||
}
|
||||
|
||||
return nil, MakeError(rt,
|
||||
fmt.Sprintf("'bindings' and 'values' size don't match. '%d' and '%d'", bindings.Count(), values.Count()))
|
||||
}
|
||||
|
||||
for i := 0; i < len(binds); i += 1 {
|
||||
// If an argument started with char `&` use it to represent
|
||||
|
@ -160,7 +178,25 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco
|
|||
//
|
||||
// for example: `(fn (x y &z) ...)`
|
||||
if binds[i].GetType() == ast.Symbol && binds[i].(*Symbol).IsRestable() {
|
||||
scope.Insert(binds[i+1].(*Symbol).GetName(), MakeList(exprs[i:]), false)
|
||||
|
||||
if i != len(binds)-1 {
|
||||
return nil, MakeErrorFor(rt, binds[i], "The function argument with '&' has to be the last argument.")
|
||||
}
|
||||
|
||||
// if the number of values are one less than the number of bindings
|
||||
// but the last binding is a Restable (e.g &x) the the last bindings
|
||||
// has to be an empty list. Note the check for number of vlaues comes
|
||||
// next.
|
||||
rest := MakeEmptyList()
|
||||
|
||||
if i == len(exprs)-1 {
|
||||
// If the number of values matches the number of bindings
|
||||
// or it is more than that create a list from them
|
||||
// to pass it to the last argument that has to be Restable (e.g &x)
|
||||
rest = MakeList(exprs[i:])
|
||||
}
|
||||
|
||||
scope.Insert(binds[i].(*Symbol).GetName()[1:], rest, false)
|
||||
break
|
||||
} else {
|
||||
scope.Insert(binds[i].(*Symbol).GetName(), exprs[i], false)
|
||||
|
|
|
@ -152,6 +152,7 @@ func requireNS(rt *Runtime, ns string) (*Namespace, IError) {
|
|||
fmt.Sprintf("The '%s' ns source code doesn't start with an 'ns' form.", ns),
|
||||
)
|
||||
}
|
||||
|
||||
namespace := MakeNS(ns, source)
|
||||
namespace.setForms(body)
|
||||
|
||||
|
|
|
@ -217,6 +217,7 @@ func readRawSymbol(parser IParsable) (IExpr, IError) {
|
|||
|
||||
return sym, nil
|
||||
}
|
||||
|
||||
func readString(parser IParsable) (IExpr, IError) {
|
||||
str := ""
|
||||
|
||||
|
@ -230,7 +231,26 @@ func readString(parser IParsable) (IExpr, IError) {
|
|||
node := MakeNode(parser.Buffer(), parser.GetLocation()-len(str), parser.GetLocation())
|
||||
return MakeString(node, str), nil
|
||||
}
|
||||
str = str + *c
|
||||
|
||||
if *c == "\\" {
|
||||
c = parser.next(false)
|
||||
switch *c {
|
||||
case "n":
|
||||
str = str + "\n"
|
||||
case "t":
|
||||
str = str + "\t"
|
||||
case "r":
|
||||
str = str + "\r"
|
||||
case "\\":
|
||||
str = str + "\\"
|
||||
case "\"":
|
||||
str = str + "\""
|
||||
default:
|
||||
return nil, makeErrorAtPoint(parser, "Unsupported escape character: \\%s", *c)
|
||||
}
|
||||
} else {
|
||||
str = str + *c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,14 +20,49 @@ package core
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Print(rt *Runtime, ast IPrintable) {
|
||||
fmt.Println(ast.String())
|
||||
func toRepresanbleString(ast ...IRepresentable) string {
|
||||
var results []string
|
||||
for _, x := range ast {
|
||||
results = append(results, x.String())
|
||||
|
||||
}
|
||||
return strings.Join(results, " ")
|
||||
}
|
||||
|
||||
func toPrintableString(ast ...IRepresentable) string {
|
||||
var results []string
|
||||
for _, x := range ast {
|
||||
|
||||
if printable, ok := x.(IPrintable); ok {
|
||||
results = append(results, printable.PrintToString())
|
||||
continue
|
||||
}
|
||||
results = append(results, x.String())
|
||||
|
||||
}
|
||||
return strings.Join(results, " ")
|
||||
}
|
||||
|
||||
func Pr(rt *Runtime, ast ...IRepresentable) {
|
||||
fmt.Print(toRepresanbleString(ast...))
|
||||
}
|
||||
|
||||
func Prn(rt *Runtime, ast ...IRepresentable) {
|
||||
fmt.Println(toRepresanbleString(ast...))
|
||||
}
|
||||
|
||||
func Print(rt *Runtime, ast ...IRepresentable) {
|
||||
fmt.Print(toPrintableString(ast...))
|
||||
}
|
||||
|
||||
func Println(rt *Runtime, ast ...IRepresentable) {
|
||||
fmt.Println(toPrintableString(ast...))
|
||||
}
|
||||
|
||||
func PrintError(rt *Runtime, err IError) {
|
||||
loc := err.GetLocation()
|
||||
fmt.Printf("Error: %s\nAt: %d to %d\n", err.String(), loc.GetStart(), loc.GetEnd())
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import (
|
|||
// 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, IError) {
|
||||
|
||||
// TODO: Add support for docstrings and meta
|
||||
switch args.Count() {
|
||||
case 2:
|
||||
|
@ -47,6 +46,10 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if value.GetType() == ast.Fn {
|
||||
value.(*Function).SetName(sym.GetName())
|
||||
}
|
||||
|
||||
ns := rt.CurrentNS()
|
||||
ns.DefineGlobal(sym.GetName(), value, true)
|
||||
return sym, nil
|
||||
|
|
|
@ -20,6 +20,7 @@ package core
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"serene-lang.org/bootstrap/pkg/ast"
|
||||
"serene-lang.org/bootstrap/pkg/hash"
|
||||
|
@ -35,6 +36,10 @@ func (s *String) GetType() ast.NodeType {
|
|||
}
|
||||
|
||||
func (s *String) String() string {
|
||||
return "\"" + s.Escape() + "\""
|
||||
}
|
||||
|
||||
func (s *String) PrintToString() string {
|
||||
return s.content
|
||||
}
|
||||
|
||||
|
@ -47,6 +52,17 @@ func (s *String) Hash() uint32 {
|
|||
return hash.HashOf(append([]byte{byte(ast.String)}, bytes...))
|
||||
}
|
||||
|
||||
func (s *String) Escape() string {
|
||||
replacer := strings.NewReplacer(
|
||||
"\n", "\\n",
|
||||
"\t", "\\t",
|
||||
"\r", "\\r",
|
||||
"\\", "\\\\",
|
||||
"\"", "\\\"",
|
||||
)
|
||||
return replacer.Replace(s.content)
|
||||
}
|
||||
|
||||
func MakeString(n Node, s string) *String {
|
||||
return &String{n, s}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,7 @@ func (s *Symbol) IsRestable() bool {
|
|||
}
|
||||
|
||||
func (s *Symbol) IsNSQualified() bool {
|
||||
if s.nsPart == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return s.nsPart != ""
|
||||
}
|
||||
|
||||
func MakeSymbol(n Node, s string) (*Symbol, IError) {
|
||||
|
|
|
@ -25,13 +25,22 @@ import (
|
|||
"serene-lang.org/bootstrap/pkg/hash"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
// IRepresentable is the interface which any value that wants to have a string
|
||||
// representation has to implement. Serene will use this string where ever
|
||||
// it needs to present a value as string.
|
||||
type IRepresentable interface {
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
// IPrintable is the interface which any value that wants to have a string
|
||||
// representation for printing has to implement. The `print` family functions will use
|
||||
// this interface to convert forms to string first and if the value doesn't
|
||||
// implement this interface they will resort to `IRepresentable`
|
||||
type IPrintable interface {
|
||||
IRepresentable
|
||||
PrintToString() string
|
||||
}
|
||||
|
||||
// IDebuggable is the interface designed for converting forms to a string
|
||||
// form which are meant to be used as debug data
|
||||
type IDebuggable interface {
|
||||
|
@ -45,7 +54,7 @@ type IExpr interface {
|
|||
ast.ILocatable
|
||||
ast.ITypable
|
||||
hash.IHashable
|
||||
IPrintable
|
||||
IRepresentable
|
||||
IDebuggable
|
||||
}
|
||||
|
||||
|
@ -60,6 +69,22 @@ func (n Node) GetLocation() ast.Location {
|
|||
return n.location
|
||||
}
|
||||
|
||||
// Helper functions ===========================================================
|
||||
|
||||
// toRepresentables converts the given collection of IExprs to an array of
|
||||
// IRepresentable. Since golangs type system is weird ( if A is an interface
|
||||
// that embeds interface B you []A should be usable as []B but that's not the
|
||||
// case in Golang), we need this convertor helper
|
||||
func toRepresentables(ast IColl) []IRepresentable {
|
||||
var params []IRepresentable
|
||||
|
||||
for _, x := range ast.ToSlice() {
|
||||
params = append(params, x.(IRepresentable))
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
// MakeNodeFromLocation creates a new Node for the given Location `loc`
|
||||
func MakeNodeFromLocation(loc ast.Location) Node {
|
||||
return Node{
|
||||
|
|
Loading…
Reference in New Issue