Skip to content

Commit

Permalink
checker: progress
Browse files Browse the repository at this point in the history
  • Loading branch information
saffage committed May 6, 2024
1 parent 2d059ea commit 2207cbb
Show file tree
Hide file tree
Showing 22 changed files with 778 additions and 827 deletions.
261 changes: 62 additions & 199 deletions ast/walk.go

Large diffs are not rendered by default.

44 changes: 17 additions & 27 deletions checker/block.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package checker

import (
"fmt"

"github.com/saffage/jet/ast"
"github.com/saffage/jet/types"
)
Expand All @@ -16,38 +14,30 @@ func NewBlock(scope *Scope) *Block {
return &Block{scope, types.Unit}
}

func (expr *Block) visit(node ast.Node) (ast.Visitor, error) {
switch node := node.(type) {
case ast.Decl:
switch decl := node.(type) {
case *ast.VarDecl:
if err := resolveVar(decl, expr.scope); err != nil {
return nil, err
}

fmt.Printf(">>> def local var `%s`\n", decl.Binding.Name)
func (check *Checker) blockVisitor(expr *Block) ast.Visitor {
return func(node ast.Node) ast.Visitor {
if decl, _ := node.(ast.Decl); decl != nil {
switch decl := decl.(type) {
case *ast.VarDecl:
check.resolveVarDecl(decl)
expr.t = types.Unit

expr.t = types.Unit
return nil, nil
case *ast.TypeAliasDecl, *ast.FuncDecl, *ast.ModuleDecl:
panic("not implemented")

case *ast.TypeAliasDecl, *ast.FuncDecl, *ast.ModuleDecl:
panic("not implemented")
default:
panic("unreachable")
}

default:
panic("unreachable")
return nil
}

default:
t, err := expr.scope.TypeOf(node)
if err != nil {
return nil, err
t := check.typeOf(node)
if t == nil {
return nil
}

expr.t = t
return nil, nil

// fmt.Printf("unchecked node: '%T'\n", node)
// expr.t = types.Unit
// return nil, nil
return nil
}
}
72 changes: 31 additions & 41 deletions checker/builtin_fns.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,58 @@ import (
"github.com/saffage/jet/types"
)

func builtInMagic(args ast.Node, scope *Scope) (*Value, error) {
argList, ok := args.(*ast.ParenList)
if !ok {
return nil, NewError(args, "expected argument list")
func (check *Checker) builtInMagic(args ast.Node, scope *Scope) *TypedValue {
argList, _ := args.(*ast.ParenList)
if argList == nil {
check.errorf(args, "expected argument list")
return nil
}

arg1, ok := argList.Exprs[0].(*ast.Literal)
if !ok {
return nil, NewError(argList.Exprs[0], "expected string literal")
arg1, _ := argList.Exprs[0].(*ast.Literal)
if arg1 == nil || arg1.Kind != ast.StringLiteral {
check.errorf(argList.Exprs[0], "expected string literal")
return nil
}

// tArg1, err := scope.TypeOf(argList.Exprs[0])
// if err != nil {
// return nil, NewError(argList.Exprs[0], "expected literal")
// }

// if !types.Primitives[types.UntypedString].Equals(tArg1) {
// return nil, NewErrorf(
// argList.Exprs[0],
// "expected 'untyped string', got '%s' instead",
// tArg1,
// )
// }

switch arg1.Value {
case "Bool":
return &Value{types.NewTypeDesc(types.Primitives[types.Bool]), nil}, nil
return &TypedValue{types.NewTypeDesc(types.Primitives[types.Bool]), nil}

case "I32":
return &Value{types.NewTypeDesc(types.Primitives[types.I32]), nil}, nil
return &TypedValue{types.NewTypeDesc(types.Primitives[types.I32]), nil}

default:
return nil, NewErrorf(arg1, "unknown magic '%s'", arg1.Value)
check.errorf(arg1, "unknown magic '%s'", arg1.Value)
return nil
}
}

func builtInTypeOf(args ast.Node, scope *Scope) (*Value, error) {
argList, ok := args.(*ast.ParenList)
if !ok {
return nil, NewError(args, "expected argument list")
func (check *Checker) builtInTypeOf(args ast.Node, scope *Scope) *TypedValue {
argList, _ := args.(*ast.ParenList)
if argList == nil {
check.errorf(args, "expected argument list")
return nil
}

arg1 := argList.Exprs[0]

t, err := scope.TypeOf(arg1)
if err != nil {
return nil, err
t := check.typeOf(argList.Exprs[0])
if t == nil {
return nil
}

return &Value{types.NewTypeDesc(types.SkipUntyped(t)), nil}, nil
return &TypedValue{types.NewTypeDesc(types.SkipUntyped(t)), nil}
}

func builtInPrint(args ast.Node, scope *Scope) (*Value, error) {
argList, ok := args.(*ast.ParenList)
if !ok {
return nil, NewError(args, "expected argument list")
func (check *Checker) builtInPrint(args ast.Node, scope *Scope) *TypedValue {
argList, _ := args.(*ast.ParenList)
if argList == nil {
check.errorf(args, "expected argument list")
return nil
}

_, err := scope.TypeOf(argList)
if err != nil {
return nil, err
t := check.typeOf(argList)
if t == nil {
return nil
}

return &Value{types.Unit, nil}, nil
return &TypedValue{types.Unit, nil}
}
16 changes: 6 additions & 10 deletions checker/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/saffage/jet/types"
)

type BuiltInFn func(args ast.Node, scope *Scope) (*Value, error)
type BuiltInFn func(args ast.Node, scope *Scope) *TypedValue

type BuiltIn struct {
name string
Expand All @@ -19,31 +19,27 @@ func (b *BuiltIn) Name() string { return b.name }
func (b *BuiltIn) Ident() *ast.Ident { return nil }
func (b *BuiltIn) Node() ast.Node { return nil }

func (b *BuiltIn) setType(t types.Type) { panic("can't change the type of the built-in function") }

var builtIns []*BuiltIn

func init() {
builtIns = []*BuiltIn{
func (check *Checker) defBuiltIns() {
check.builtIns = []*BuiltIn{
{
name: "magic",
f: builtInMagic,
f: check.builtInMagic,
t: types.NewFunc(
types.NewTuple(types.Primitives[types.AnyTypeDesc]),
types.NewTuple(types.Primitives[types.UntypedString]),
),
},
{
name: "type_of",
f: builtInTypeOf,
f: check.builtInTypeOf,
t: types.NewFunc(
types.NewTuple(types.Primitives[types.AnyTypeDesc]),
types.NewTuple(types.Primitives[types.Any]),
),
},
{
name: "print",
f: builtInPrint,
f: check.builtInPrint,
t: types.NewFunc(
types.Unit,
types.NewTuple(types.Primitives[types.Any]),
Expand Down
135 changes: 131 additions & 4 deletions checker/checker.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,134 @@
package checker

// type Checker struct {
// errors []error
// }
import (
"os"

// func (check *Checker) Errors() []error { return check.errors }
"github.com/davecgh/go-spew/spew"
"github.com/saffage/jet/ast"
"github.com/saffage/jet/internal/assert"
"github.com/saffage/jet/types"
)

type Checker struct {
types map[ast.Node]TypedValue
defs map[*ast.Ident]Symbol
uses map[*ast.Ident]Symbol

module *Module
scope *Scope
builtIns []*BuiltIn
errors []error
isErrorHandled bool
}

func Check(node *ast.ModuleDecl) []error {
module := NewModule(node)
check := &Checker{
types: make(map[ast.Node]TypedValue),
defs: make(map[*ast.Ident]Symbol),
uses: make(map[*ast.Ident]Symbol),
module: module,
scope: module.scope,
errors: make([]error, 0),
isErrorHandled: true,
}

check.defBuiltIns()

{
nodes := []ast.Node(nil)

switch body := node.Body.(type) {
case *ast.List:
nodes = body.Nodes

case *ast.CurlyList:
nodes = body.List.Nodes

default:
panic("ill-formed AST")
}

for _, node := range nodes {
ast.WalkTopDown(check.visit, node)
}

module.completed = true
}

{
f, err := os.Create("checker_state.txt")
if err != nil {
panic(err)
}
defer f.Close()
spew.Fdump(f, check)
}

return check.errors
}

// Type checks 'expr' and returns its type.
// If error was occured, result is undefined
func (check *Checker) typeOf(expr ast.Node) types.Type {
if t, ok := check.types[expr]; ok {
return t.Type
}

if v := check.valueOfInternal(expr); v != nil {
check.setValue(expr, *v)
return v.Type
}

t := check.typeOfInternal(expr)
check.setType(expr, t)
return t
}

func (check *Checker) valueOf(expr ast.Node) *TypedValue {
if t, ok := check.types[expr]; ok {
return &t
}

if value := check.valueOfInternal(expr); value != nil {
check.setValue(expr, *value)
return value
}

return nil
}

func (check *Checker) setType(expr ast.Node, t types.Type) {
assert.Ok(expr != nil)
assert.Ok(t != nil)

if check.types != nil {
check.types[expr] = TypedValue{t, nil}
}
}

func (check *Checker) setValue(expr ast.Node, value TypedValue) {
assert.Ok(expr != nil)
assert.Ok(value.Type != nil)

if check.types != nil {
check.types[expr] = value
}
}

func (check *Checker) newDef(ident *ast.Ident, sym Symbol) {
assert.Ok(ident != nil)

if check.defs != nil {
check.defs[ident] = sym
}
}

func (check *Checker) newUse(ident *ast.Ident, sym Symbol) {
assert.Ok(ident != nil)
assert.Ok(sym != nil)

if check.uses != nil {
check.uses[ident] = sym
}
}
18 changes: 11 additions & 7 deletions checker/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ import (
type Error struct {
Message string
Node ast.Node
Notes []Error
Notes []*Error
}

func NewError(node ast.Node, message string) Error {
return Error{
func NewError(node ast.Node, message string) *Error {
return &Error{
Message: message,
Node: node,
}
}

func NewErrorf(node ast.Node, format string, args ...any) Error {
return Error{
func NewErrorf(node ast.Node, format string, args ...any) *Error {
return &Error{
Message: fmt.Sprintf(format, args...),
Node: node,
}
}

func (err Error) Error() string {
return err.Message
func (err *Error) Error() string { return err.Message }

func (check *Checker) errorf(node ast.Node, format string, args ...any) {
err := NewErrorf(node, format, args...)
check.errors = append(check.errors, err)
check.isErrorHandled = false
}
Loading

0 comments on commit 2207cbb

Please sign in to comment.