diff --git a/README.md b/README.md index 02f0fab..7200803 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ - `each` - `while` - `repeat` -- `[...]` - `:` - `~` - `keep` diff --git a/main.go b/main.go index 60d2574..bb16468 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,9 @@ package main import ( "fmt" + "io/ioutil" "log" + "os" "strings" "github.com/chzyer/readline" @@ -11,7 +13,7 @@ import ( "github.com/hellerve/argos/parser" ) -func main() { +func runRepl() { rl, err := readline.New("argos> ") if err != nil { @@ -22,13 +24,13 @@ func main() { e := eval.ParentEnv() for { - prompt, err := rl.Readline() + input, err := rl.Readline() if err != nil { return } - parsed, err, unconsumed := parser.Parse(prompt) + parsed, err, unconsumed := parser.Parse(input) if err != nil { fmt.Println(err) @@ -52,3 +54,43 @@ func main() { fmt.Println(evald.Pretty()) } } + +func runFile(path string) { + e := eval.ParentEnv() + input, err := ioutil.ReadFile(path) + + if err != nil { + fmt.Println(err) + return + } + + parsed, err, unconsumed := parser.Parse(string(input)) + + if err != nil { + fmt.Println(err) + return + } + + if len(unconsumed) != 0 { + fmt.Println("Unconsumed input:", strings.Join(unconsumed, " ")) + return + } + + evald, err := eval.Eval(parsed, e) + + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(evald.Pretty()) +} + +func main() { + args := os.Args + if len(args) == 1 { + runRepl() + } else { + runFile(os.Args[1]) + } +} diff --git a/parser/parser.go b/parser/parser.go index d12ec6b..498b083 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -2,6 +2,7 @@ package parser import ( "errors" + "regexp" "strconv" "strings" @@ -18,8 +19,18 @@ func withoutEmpty(input []string) []string { return r } +var comments = regexp.MustCompile(";.*") +var whitespace = regexp.MustCompile("\\s") + +func explode(s string, start string, end string) string { + return strings.Replace(strings.Replace(s, start, " "+start+" ", -1), end, " "+end+" ", -1) +} + func tokenize(input string) []string { - return withoutEmpty(strings.Split(strings.Replace(strings.Replace(input, "(", " ( ", -1), ")", " ) ", -1), " ")) + withoutComments := string(comments.ReplaceAll([]byte(input), []byte(""))) + explodedParens := explode(withoutComments, "(", ")") + explodedBrackets := explode(explodedParens, "[", "]") + return withoutEmpty(whitespace.Split(explodedBrackets, -1)) } func parseValue(input []string) (*ast.AST, error, []string) { @@ -53,9 +64,16 @@ func parseValue(input []string) (*ast.AST, error, []string) { return &res, nil, input[1:] } +func makeFn(bodyStatements []ast.AST) ast.AST { + body := ast.AST{ast.List, bodyStatements} + args := ast.AST{ast.List, []ast.AST{ast.AST{ast.Symbol, "_"}}} + + return ast.AST{ast.List, []ast.AST{ast.AST{ast.Symbol, "fn"}, args, body}} +} + func parseToken(input []string) (*ast.AST, error, []string) { if len(input) == 0 { - return nil, errors.New("Unmatched '('"), input + return nil, errors.New("Unmatched '(' or '['"), input } if input[0][0] == '\'' { @@ -94,6 +112,25 @@ func parseToken(input []string) (*ast.AST, error, []string) { case ")": { return nil, errors.New("Unmatched ')'"), input } + case "[": { + var l []ast.AST + input = input[1:] + for input[0] != "]" { + elem, err, newInput := parseToken(input) + + if err != nil { + return nil, err, input + } + + l = append(l, *elem) + input = newInput + } + res := makeFn(l) + return &res, nil, input[1:] + } + case "]": { + return nil, errors.New("Unmatched ']'"), input + } } return parseValue(input) }