add file trace to parser, refactor, fix inner type
This commit is contained in:
parent
bcf0978b68
commit
c8660a6471
10 changed files with 131 additions and 51 deletions
46
cli/main.go
46
cli/main.go
|
|
@ -9,6 +9,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
|
|
@ -21,24 +22,28 @@ type RunCmd struct {
|
|||
File string `arg:"" name:"file" help:"File to read program from" type:"existingfile"`
|
||||
}
|
||||
|
||||
// WorkingDirectoryResolver resolves imports relative to the working directory
|
||||
type WorkingDirectoryResolver struct {
|
||||
workingDirectory string
|
||||
// DirectoryResolver resolves imports from several directories
|
||||
type DirectoryResolver struct {
|
||||
// directories to search through, in decreasing priority.
|
||||
directories []string
|
||||
}
|
||||
|
||||
func (r *WorkingDirectoryResolver) Resolve(path string) (string, error) {
|
||||
pth := filepath.Join(r.workingDirectory, path)
|
||||
f, err := os.ReadFile(pth)
|
||||
if err != nil {
|
||||
return "", err
|
||||
func (r *DirectoryResolver) Resolve(path string) (string, error) {
|
||||
for _, d := range r.directories {
|
||||
pth := filepath.Join(d, path)
|
||||
f, err := os.ReadFile(pth)
|
||||
|
||||
if err == nil {
|
||||
return string(f), nil
|
||||
}
|
||||
}
|
||||
|
||||
return string(f), nil
|
||||
return "", errors.New("couldn't resolve import")
|
||||
}
|
||||
|
||||
func (r *WorkingDirectoryResolver) IsSame(a, b string) bool {
|
||||
apath := filepath.Clean(filepath.Join(r.workingDirectory, a))
|
||||
bpath := filepath.Clean(filepath.Join(r.workingDirectory, b))
|
||||
func (r *DirectoryResolver) IsSame(a, b string) bool {
|
||||
apath := filepath.Clean(a)
|
||||
bpath := filepath.Clean(b)
|
||||
|
||||
return apath == bpath
|
||||
}
|
||||
|
|
@ -57,7 +62,7 @@ func makeChunk(ctx *Context, fpath string, ignoreWarnings bool) (*core.Chunk, er
|
|||
src := string(f)
|
||||
|
||||
if ctx.Debug {
|
||||
log.Println("Initialized lexer")
|
||||
log.Println("Initializing lexer")
|
||||
}
|
||||
l := core.NewLexer(src)
|
||||
|
||||
|
|
@ -78,7 +83,7 @@ func makeChunk(ctx *Context, fpath string, ignoreWarnings bool) (*core.Chunk, er
|
|||
log.Printf("Lexed %d tokens", len(tokens))
|
||||
|
||||
}
|
||||
p := core.NewParser(src, tokens)
|
||||
p := core.NewParser(src, []string{fpath}, tokens)
|
||||
|
||||
if ctx.Debug {
|
||||
log.Println("Initialized parser")
|
||||
|
|
@ -110,9 +115,14 @@ func makeChunk(ctx *Context, fpath string, ignoreWarnings bool) (*core.Chunk, er
|
|||
log.Println("Setting imports resolver")
|
||||
}
|
||||
|
||||
lib := os.Getenv("ANGLAIS_PATH")
|
||||
dirs := strings.Split(lib, ":")
|
||||
|
||||
dir, _ := path.Split(fpath)
|
||||
c.SetImportsResolver(&WorkingDirectoryResolver{
|
||||
dir,
|
||||
c.SetImportsResolver(&DirectoryResolver{
|
||||
append([]string{
|
||||
dir,
|
||||
}, dirs...),
|
||||
})
|
||||
|
||||
if ctx.Debug {
|
||||
|
|
@ -231,7 +241,7 @@ type ReplCmd struct {
|
|||
}
|
||||
|
||||
func (cmd *ReplCmd) Run(ctx *Context) error {
|
||||
c := core.NewCompiler([]rune(""))
|
||||
c := core.NewCompiler([]rune{})
|
||||
vm := core.NewVM(core.NewChunk([]core.Bytecode{}, []core.Value{}), 256, 256)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
|
@ -249,7 +259,7 @@ func (cmd *ReplCmd) Run(ctx *Context) error {
|
|||
continue
|
||||
}
|
||||
|
||||
p := core.NewParser(src, tokens)
|
||||
p := core.NewParser(src, []string{}, tokens)
|
||||
prog, err := p.Parse("REPL")
|
||||
if err != nil {
|
||||
var e core.FormatedError
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ func TestAll(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Log("Initializing parser")
|
||||
p := NewParser(tc.src, tokens)
|
||||
p := NewParser(tc.src, []string{}, tokens)
|
||||
|
||||
t.Log("Parsing tokens")
|
||||
tree, err := p.Parse(tc.src)
|
||||
|
|
@ -173,7 +173,7 @@ func BenchmarkAll(b *testing.B) {
|
|||
l := NewLexer(tc.src)
|
||||
tokens, _ := l.Tokenize()
|
||||
|
||||
p := NewParser(tc.src, tokens)
|
||||
p := NewParser(tc.src, []string{}, tokens)
|
||||
tree, _ := p.Parse(tc.src)
|
||||
|
||||
c := NewCompiler([]rune(tc.src))
|
||||
|
|
|
|||
|
|
@ -393,6 +393,18 @@ func (c *Compiler) compile(tree Node) error {
|
|||
return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n)
|
||||
}
|
||||
|
||||
var innerType TypeSignature
|
||||
if a, ok := n.source.(*AccessNode); ok {
|
||||
isig, err := c.deduceSignature(a.source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isig.Type() == TypeList {
|
||||
innerType = isig.(*ListSignature).Contents
|
||||
}
|
||||
}
|
||||
|
||||
for i, arg := range n.args {
|
||||
sig, err := c.deduceSignature(arg)
|
||||
if err != nil {
|
||||
|
|
@ -400,7 +412,11 @@ func (c *Compiler) compile(tree Node) error {
|
|||
}
|
||||
|
||||
// check that arg type is as required
|
||||
if !f.In[i].Matches(sig) {
|
||||
fin := f.In[i]
|
||||
if fin.Type() == TypeInner && innerType != nil {
|
||||
fin = innerType
|
||||
}
|
||||
if !fin.Matches(sig) {
|
||||
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
|
||||
}
|
||||
|
||||
|
|
@ -598,7 +614,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
n := tree.(*ListNode)
|
||||
|
||||
contents := n.content
|
||||
// check for contents type
|
||||
// check for content type
|
||||
for _, v := range n.items {
|
||||
sig, err := c.deduceSignature(v)
|
||||
if err != nil {
|
||||
|
|
@ -761,7 +777,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
|
||||
if f.Out.Type() == TypeInner {
|
||||
if innerType == nil {
|
||||
return nil, c.error(fmt.Sprintf("function source (%s) has no inner type", sig), n.source)
|
||||
return nil, c.error(fmt.Sprintf("function source (%s) has no inner type (@ output)", sig), n.source)
|
||||
}
|
||||
|
||||
return innerType, nil
|
||||
|
|
@ -1204,7 +1220,7 @@ func (c *Compiler) error(msg string, causer Node) CompilerError {
|
|||
msg,
|
||||
causer,
|
||||
c.source,
|
||||
c.fileStack.items[0:c.fileStack.Current],
|
||||
c.fileStack.Slice(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1223,7 +1239,7 @@ func (c *Compiler) resolveImport(path string) error {
|
|||
// stop recursive imports
|
||||
for i := c.fileStack.Current - 1; i >= 0; i-- {
|
||||
if c.resolver.IsSame(path, c.fileStack.items[i]) {
|
||||
return errors.New("recursive imports")
|
||||
return errors.New("recursive import")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1238,7 +1254,7 @@ func (c *Compiler) resolveImport(path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
parser := NewParser(src, tokens)
|
||||
parser := NewParser(src, append(c.fileStack.Slice(), path), tokens)
|
||||
p, err := parser.Parse(path)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ func (t TokenType) String() string {
|
|||
return "hexadecimal"
|
||||
}
|
||||
|
||||
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
||||
panic("UNDEFINED TOKENTYPE STRING CONVERSION")
|
||||
}
|
||||
|
||||
type Lexer struct {
|
||||
|
|
@ -320,9 +320,9 @@ func (l *Lexer) NextToken() (Token, error) {
|
|||
return l.makeToken(TokenString), nil
|
||||
|
||||
default:
|
||||
if unicode.IsLetter(c) || c == '_' {
|
||||
if l.isAlpha(c) {
|
||||
// assemble variable
|
||||
for l.isAlpha(l.peek()) {
|
||||
for l.isAlphaNumeric(l.peek()) {
|
||||
l.advance()
|
||||
}
|
||||
|
||||
|
|
@ -435,7 +435,11 @@ func (l *Lexer) accept(c rune) bool {
|
|||
}
|
||||
|
||||
func (l *Lexer) isAlpha(c rune) bool {
|
||||
return unicode.IsLetter(c) || unicode.IsDigit(c) || c == '_'
|
||||
return unicode.IsLetter(c) || c == '_'
|
||||
}
|
||||
|
||||
func (l *Lexer) isAlphaNumeric(c rune) bool {
|
||||
return l.isAlpha(c) || unicode.IsDigit(c)
|
||||
}
|
||||
|
||||
func (l *Lexer) advance() {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ type ParsingError struct {
|
|||
Description string
|
||||
Causer *Token
|
||||
Source string
|
||||
Trace []string
|
||||
}
|
||||
|
||||
func (p ParsingError) Error() string {
|
||||
|
|
@ -26,7 +27,7 @@ func (p ParsingError) Error() string {
|
|||
// Format Print a rich and informative error
|
||||
func (p ParsingError) Format() string {
|
||||
src := []rune(p.Source)
|
||||
builder := strings.Builder{}
|
||||
b := strings.Builder{}
|
||||
|
||||
lineNumber := 1
|
||||
lineBeginning := 0
|
||||
|
|
@ -46,39 +47,46 @@ func (p ParsingError) Format() string {
|
|||
}
|
||||
|
||||
descriptor := fmt.Sprintf("%d:%d", lineNumber, int(p.Causer.Start)-lineBeginning+1)
|
||||
builder.WriteString(p.Description)
|
||||
builder.WriteRune('\n')
|
||||
b.WriteString(p.Description)
|
||||
b.WriteRune('\n')
|
||||
|
||||
builder.WriteString(descriptor)
|
||||
builder.WriteString(" | ")
|
||||
builder.WriteString(string(src[lineBeginning:lineEnd]))
|
||||
b.WriteString(descriptor)
|
||||
b.WriteString(" | ")
|
||||
b.WriteString(string(src[lineBeginning:lineEnd]))
|
||||
|
||||
builder.WriteString("\n")
|
||||
builder.WriteString(strings.Repeat(" ", len(descriptor)))
|
||||
builder.WriteString(" ")
|
||||
b.WriteString("\n")
|
||||
b.WriteString(strings.Repeat(" ", len(descriptor)))
|
||||
b.WriteString(" ")
|
||||
for i := lineBeginning; i <= int(p.Causer.Start); i++ {
|
||||
builder.WriteRune(' ')
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
|
||||
for i := 0; i < int(p.Causer.Length); i++ {
|
||||
builder.WriteRune('^')
|
||||
b.WriteRune('^')
|
||||
}
|
||||
builder.WriteRune('\n')
|
||||
b.WriteRune('\n')
|
||||
b.WriteRune('\n')
|
||||
|
||||
return builder.String()
|
||||
for i := len(p.Trace) - 1; i >= 0; i-- {
|
||||
b.WriteString(fmt.Sprintf("[%d] %s\n", i, p.Trace[i]))
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
source string
|
||||
trace []string
|
||||
tokens []Token
|
||||
prev *Token
|
||||
curr *Token
|
||||
pos Pos
|
||||
}
|
||||
|
||||
func NewParser(source string, tokens []Token) *Parser {
|
||||
func NewParser(source string, trace []string, tokens []Token) *Parser {
|
||||
return &Parser{
|
||||
source: source,
|
||||
trace: trace,
|
||||
tokens: tokens,
|
||||
pos: 0,
|
||||
}
|
||||
|
|
@ -121,6 +129,7 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
|||
}
|
||||
|
||||
imports = append(imports, p.prev.Lexeme[1:len(p.prev.Lexeme)-1])
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := p.block(true)
|
||||
|
|
@ -190,6 +199,7 @@ func (p *Parser) error(error string, causer *Token) error {
|
|||
Description: error,
|
||||
Causer: causer,
|
||||
Source: p.source,
|
||||
Trace: p.trace,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
func TestNewParser(t *testing.T) {
|
||||
tokens := make([]Token, 0)
|
||||
|
||||
p := NewParser("", tokens)
|
||||
p := NewParser("", []string{}, tokens)
|
||||
|
||||
if p == nil {
|
||||
t.Fatal("parser should not be nil")
|
||||
|
|
@ -34,7 +34,7 @@ func TestNewParser(t *testing.T) {
|
|||
func BenchmarkNewParser(b *testing.B) {
|
||||
tokens := make([]Token, 0)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = NewParser("", tokens)
|
||||
_ = NewParser("", []string{}, tokens)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -910,7 +910,7 @@ func TestParser_Parse(t *testing.T) {
|
|||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Logf("Initializing parser")
|
||||
p := NewParser("", data.tokens)
|
||||
p := NewParser("", []string{}, data.tokens)
|
||||
|
||||
t.Logf("Parsing main")
|
||||
tree, err := p.Parse("")
|
||||
|
|
@ -931,7 +931,7 @@ func BenchmarkParser_Parse(b *testing.B) {
|
|||
for name, data := range tokenData {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := NewParser("", data.tokens)
|
||||
p := NewParser("", []string{}, data.tokens)
|
||||
|
||||
_, _ = p.Parse("")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,3 +56,8 @@ func (s *Stack[T]) check() {
|
|||
panic("stack underflow")
|
||||
}
|
||||
}
|
||||
|
||||
// Slice gets a slice of the current items in use
|
||||
func (s *Stack[T]) Slice() []T {
|
||||
return s.items[:s.Current]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -467,6 +467,31 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
nil,
|
||||
false,
|
||||
},
|
||||
"put": {
|
||||
"put",
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&NumberSignature{}, &InnerSignature{},
|
||||
},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, args []Value) (Value, error) {
|
||||
l := this.(*ListValue)
|
||||
i := int(args[0].(*NumberValue).Number)
|
||||
v := args[1]
|
||||
|
||||
// bounds check
|
||||
if i < 0 || i >= len(l.Items) {
|
||||
return nil, errors.New(fmt.Sprintf("index %x out of range", i))
|
||||
}
|
||||
|
||||
l.Items[i] = v
|
||||
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
"length": {
|
||||
"length",
|
||||
&FunctionSignature{
|
||||
|
|
|
|||
12
test_all.sh
12
test_all.sh
|
|
@ -1,9 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo '=== Building WASM lib ==='
|
||||
cd wasm || exit 1
|
||||
if ! GOOS=js GOARCH=wasm go build .; then
|
||||
echo "=x= Had error building WASM lib =x="
|
||||
exit 1
|
||||
else
|
||||
echo "=+= Successfully built WASM lib =+="
|
||||
fi
|
||||
cd ..
|
||||
|
||||
echo '=== Building CLI ==='
|
||||
cd cli || exit 1
|
||||
if ! go build .; then
|
||||
echo "=== Had error building CLI ==="
|
||||
echo "=x= Had error building CLI =x="
|
||||
exit 1
|
||||
else
|
||||
echo "=+= Successfully built CLI =+="
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build wasm && go1.23
|
||||
//go:build wasm && go1.24
|
||||
|
||||
package main
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ func run(_ js.Value, args []js.Value) interface{} {
|
|||
|
||||
log.Printf("got tokens: %v", tokens)
|
||||
|
||||
parser := core.NewParser(source, tokens)
|
||||
parser := core.NewParser(source, []string{}, tokens)
|
||||
|
||||
tree, err := parser.Parse(source)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue