Skip to content

Commit

Permalink
feat: native internal libs
Browse files Browse the repository at this point in the history
  • Loading branch information
pmqueiroz committed Nov 25, 2024
1 parent 67a9eb2 commit 632311d
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 42 deletions.
6 changes: 3 additions & 3 deletions cli/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import (
"fmt"
"os"

"github.com/pmqueiroz/umbra/interpreter"
"github.com/pmqueiroz/umbra/environment"
)

func header() {
fmt.Print("Welcome to Umbra REPL!\nEnter :q to exit.\n")
}

func Repl(evaluate func(content string, env *interpreter.Environment)) {
func Repl(evaluate func(content string, env *environment.Environment)) {
header()
reader := bufio.NewReader(os.Stdin)
env := interpreter.NewEnvironment(nil)
env := environment.NewEnvironment(nil)

for {
fmt.Print("> ")
Expand Down
17 changes: 9 additions & 8 deletions interpreter/environment.go → environment/environment.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package interpreter
package environment

import (
"fmt"
Expand All @@ -9,10 +9,11 @@ import (
)

type Variable struct {
data interface{}
dataType tokens.TokenType
Data interface{}
DataType tokens.TokenType
Nullable bool
private bool
nullable bool
native bool
}

type Namespace struct {
Expand Down Expand Up @@ -50,7 +51,7 @@ func (env *Environment) Get(name string, allowPrivate bool) (Variable, bool) {

func (env *Environment) Set(name string, value interface{}) bool {
if val, exists := env.values[name]; exists {
env.values[name] = Variable{data: value, dataType: val.dataType, private: val.private, nullable: val.nullable}
env.values[name] = Variable{Data: value, DataType: val.DataType, private: val.private, Nullable: val.Nullable}
return true
}
if env.parent != nil {
Expand All @@ -59,21 +60,21 @@ func (env *Environment) Set(name string, value interface{}) bool {
return false
}

func (env *Environment) Create(name string, value interface{}, dataType tokens.TokenType, nullable bool) bool {
func (env *Environment) Create(name string, value interface{}, dataType tokens.TokenType, nullable bool, internal bool) bool {
if _, exists := env.Get(name, true); exists {
fmt.Println(exception.NewRuntimeError("RT001", name))
os.Exit(1)
return false
}
env.values[name] = Variable{data: value, dataType: dataType, private: true, nullable: nullable}
env.values[name] = Variable{Data: value, DataType: dataType, private: true, Nullable: nullable, native: internal}
return true
}

func (env *Environment) ListValues(includePrivate bool) map[string]interface{} {
allValues := make(map[string]interface{})
for key, value := range env.values {
if !value.private || includePrivate {
allValues[key] = value.data
allValues[key] = value.Data
}
}
if env.parent != nil {
Expand Down
2 changes: 2 additions & 0 deletions exception/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ var Messages = map[string]string{
"RT028": "cannot convert value of type %s to type %s",
"RT029": "<str> should have only on char to be converted to type <char>",
"RT030": "convert into char failed",
"RT031": "invalid internal function call",
"RT032": "cannot read file: %s",
"GN001": "cannot find module '%s'",
}
35 changes: 27 additions & 8 deletions interpreter/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"unicode/utf8"

"github.com/pmqueiroz/umbra/ast"
"github.com/pmqueiroz/umbra/environment"
"github.com/pmqueiroz/umbra/exception"
"github.com/pmqueiroz/umbra/native"
"github.com/pmqueiroz/umbra/tokens"
"github.com/pmqueiroz/umbra/types"
"github.com/sanity-io/litter"
Expand Down Expand Up @@ -46,7 +48,7 @@ func toFloat64(value interface{}) (float64, error) {
}
}

func Evaluate(expression ast.Expression, env *Environment) (interface{}, error) {
func Evaluate(expression ast.Expression, env *environment.Environment) (interface{}, error) {
switch expr := expression.(type) {
case ast.LiteralExpression:
return expr.Value, nil
Expand All @@ -59,7 +61,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)
if !ok {
return nil, exception.NewRuntimeError("RT002", expr.Name.Lexeme)
}
return value.data, nil
return value.Data, nil
case ast.AssignExpression:
value, err := Evaluate(expr.Value, env)
if err != nil {
Expand All @@ -74,7 +76,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)
return nil, exception.NewRuntimeError("RT002", target.Name.Lexeme)
}

typeErr := types.CheckType(variable.dataType, value, variable.nullable)
typeErr := types.CheckType(variable.DataType, value, variable.Nullable)

if typeErr != nil {
return nil, typeErr
Expand Down Expand Up @@ -285,7 +287,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)
}

if function, ok := callee.(FunctionDeclaration); ok {
funcEnv := NewEnvironment(function.Environment)
funcEnv := environment.NewEnvironment(function.Environment)

for i, param := range function.Itself.Params {
if param.Variadic {
Expand All @@ -303,7 +305,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)

variadicArgs = append(variadicArgs, argValue)
}
funcEnv.Create(param.Name.Lexeme, variadicArgs, param.Type.Type, param.Nullable)
funcEnv.Create(param.Name.Lexeme, variadicArgs, param.Type.Type, param.Nullable, false)
break
} else {
argValue, err := Evaluate(expr.Arguments[i], env)
Expand All @@ -316,7 +318,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)
return nil, typeErr
}

funcEnv.Create(param.Name.Lexeme, argValue, param.Type.Type, param.Nullable)
funcEnv.Create(param.Name.Lexeme, argValue, param.Type.Type, param.Nullable, false)
}
}

Expand All @@ -333,6 +335,23 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)
}

return result, nil
} else if goFunc, ok := callee.(native.InternalModuleFn); ok {
var args []interface{}
for _, arg := range expr.Arguments {
argValue, err := Evaluate(arg, env)
if err != nil {
return nil, err
}
args = append(args, argValue)
}

defer func() {
if r := recover(); r != nil {
err = exception.NewRuntimeError("RT031")
}
}()
result, err := goFunc(args)
return result, err
}

return nil, exception.NewRuntimeError("RT014", expr.Callee)
Expand Down Expand Up @@ -442,7 +461,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)

value, _ := namespace.Get(expr.Property.Lexeme, false)

return value.data, nil
return value.Data, nil
}

return nil, exception.NewRuntimeError("RT019", litter.Sdump(expr.Namespace))
Expand Down Expand Up @@ -511,7 +530,7 @@ func Evaluate(expression ast.Expression, env *Environment) (interface{}, error)
}
}

func resolveMemberExpressionProperty(expr ast.MemberExpression, env *Environment) (interface{}, error) {
func resolveMemberExpressionProperty(expr ast.MemberExpression, env *environment.Environment) (interface{}, error) {
var property interface{}
var computeErr error
if expr.Computed {
Expand Down
28 changes: 13 additions & 15 deletions interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/pmqueiroz/umbra/ast"
"github.com/pmqueiroz/umbra/environment"
"github.com/pmqueiroz/umbra/exception"
"github.com/pmqueiroz/umbra/tokens"
"github.com/pmqueiroz/umbra/types"
Expand Down Expand Up @@ -34,7 +35,7 @@ func (r Continue) Error() string {

type FunctionDeclaration struct {
Itself *ast.FunctionStatement
Environment *Environment
Environment *environment.Environment
}

func extractVarName(stmt ast.Statement) string {
Expand All @@ -46,7 +47,7 @@ func extractVarName(stmt ast.Statement) string {
}
}

func Interpret(statement ast.Statement, env *Environment) error {
func Interpret(statement ast.Statement, env *environment.Environment) error {
switch stmt := statement.(type) {
case ast.PrintStatement:
value, err := Evaluate(stmt.Expression, env)
Expand Down Expand Up @@ -89,10 +90,10 @@ func Interpret(statement ast.Statement, env *Environment) error {
}
}

env.Create(stmt.Name.Lexeme, value, stmt.Type.Type, stmt.Nullable)
env.Create(stmt.Name.Lexeme, value, stmt.Type.Type, stmt.Nullable, false)
return nil
case ast.BlockStatement:
newEnv := NewEnvironment(env)
newEnv := environment.NewEnvironment(env)
for _, stmt := range stmt.Statements {
if err := Interpret(stmt, newEnv); err != nil {
return err
Expand Down Expand Up @@ -125,21 +126,21 @@ func Interpret(statement ast.Statement, env *Environment) error {
}
return Return{value: value}
case ast.FunctionStatement:
env.Create(stmt.Name.Lexeme, FunctionDeclaration{Itself: &stmt, Environment: env}, tokens.FUN_TYPE, false)
env.Create(stmt.Name.Lexeme, FunctionDeclaration{Itself: &stmt, Environment: env}, tokens.FUN_TYPE, false, false)
return nil
case ast.ExpressionStatement:
_, err := Evaluate(stmt.Expression, env)
return err
case ast.InitializedForStatement:
forEnv := NewEnvironment(env)
forEnv := environment.NewEnvironment(env)
if err := Interpret(stmt.Start, forEnv); err != nil {
return err
}

initializedVarName := extractVarName(stmt.Start)

for {
loopEnv := NewEnvironment(forEnv)
loopEnv := environment.NewEnvironment(forEnv)
controlVar, exists := loopEnv.Get(initializedVarName, true)
if !exists {
return exception.NewRuntimeError("RT021", initializedVarName)
Expand All @@ -152,7 +153,7 @@ func Interpret(statement ast.Statement, env *Environment) error {

var condition bool
if parsedStop, ok := stop.(float64); ok {
condition = controlVar.data.(float64) <= parsedStop
condition = controlVar.Data.(float64) <= parsedStop
} else {
return exception.NewRuntimeError("RT022", types.ParseUmbraType(stop))
}
Expand All @@ -173,7 +174,7 @@ func Interpret(statement ast.Statement, env *Environment) error {
return exception.NewRuntimeError("RT023", types.ParseUmbraType(stepValue))
}

loopEnv.Set(initializedVarName, controlVar.data.(float64)+step)
loopEnv.Set(initializedVarName, controlVar.Data.(float64)+step)

if _, ok := bodyErr.(Break); ok {
break
Expand All @@ -191,7 +192,7 @@ func Interpret(statement ast.Statement, env *Environment) error {
return nil
case ast.ConditionalForStatement:
for {
loopEnv := NewEnvironment(env)
loopEnv := environment.NewEnvironment(env)

condition, err := Evaluate(stmt.Condition, loopEnv)
if err != nil {
Expand Down Expand Up @@ -234,14 +235,11 @@ func Interpret(statement ast.Statement, env *Environment) error {
}
return nil
case ast.ImportStatement:
namespace, err := LoadModule(stmt.Path.Lexeme)

module, err := LoadModule(stmt.Path.Lexeme)
if err != nil {
return err
}

env.CreateNamespace(stmt.Path.Lexeme, &namespace)

env.CreateNamespace(module.Name, module.Environment)
return nil
default:
return exception.NewRuntimeError("RT000", litter.Sdump(statement))
Expand Down
51 changes: 46 additions & 5 deletions interpreter/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,36 @@ import (
"os"

"github.com/pmqueiroz/umbra/ast"
"github.com/pmqueiroz/umbra/environment"
"github.com/pmqueiroz/umbra/helpers"
"github.com/pmqueiroz/umbra/native"
"github.com/pmqueiroz/umbra/tokens"
)

type Module struct {
Name string
Environment *environment.Environment
}

func ResolveModule(module string) (string, error) {
content, err := helpers.ReadFile(fmt.Sprintf("%s/lib/%s.u", os.Getenv("UMBRA_PATH"), module))

return content, err
}

func LoadModule(path string) (Environment, error) {
func LoadInternalModule(name string, namespace *environment.Environment) error {
if ok := native.Register(name, namespace); !ok {
return fmt.Errorf("unable to include %s. internal module does not exits", name)
}

return nil
}

func LoadBuiltInModule(path string, namespace *environment.Environment) error {
content, err := ResolveModule(path)

if err != nil {
return Environment{}, fmt.Errorf("unable to include %s. module does not exits", path)
return fmt.Errorf("unable to include %s. module does not exits", path)
}

tokens, err := tokens.Tokenize(content)
Expand All @@ -30,11 +45,37 @@ func LoadModule(path string) (Environment, error) {

module := ast.Parse(tokens)

namespace := NewEnvironment(nil)

if err := Interpret(module, namespace); err != nil {
fmt.Println(err)
}

return *namespace, nil
return nil
}

func LoadModule(path string) (Module, error) {
namespace := environment.NewEnvironment(nil)

if len(path) >= 9 && path[:9] == "internal/" {
err := LoadInternalModule(path[9:], namespace)

if err != nil {
return Module{}, err
}

return Module{
Name: path[9:],
Environment: namespace,
}, nil
}

err := LoadBuiltInModule(path, namespace)

if err != nil {
return Module{}, err
}

return Module{
Name: path,
Environment: namespace,
}, nil
}
Loading

0 comments on commit 632311d

Please sign in to comment.