Skip to content

Commit

Permalink
Release v0.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kscarlett committed Aug 30, 2018
2 parents 27f7362 + 7579b4b commit 2c1769e
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ matrix:
include:
- go: 1.x
env: LATEST=true
- go: 1.7.x
- go: 1.8.x
- go: 1.9.x
- go: 1.10.x
- go: 1.11.x
- go: tip
allow_failures:
- go: tip
Expand Down
26 changes: 26 additions & 0 deletions cmd/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"fmt"
"os"
"runtime"

"github.com/spf13/cobra"
)

var env = &cobra.Command{
Use: "env",
Short: "Displays the current environment setup used by Hussar",
Run: func(cmd *cobra.Command, args []string) {
printEnvironment()
},
}

func printEnvironment() {
fmt.Printf("arch: %s\n", runtime.GOARCH)
fmt.Printf("os: %s\n", runtime.GOOS)
fmt.Printf("bin: %s\n", os.Args[0])
fmt.Printf("gc: %s\n", runtime.Version())
fmt.Printf("vers: %s\n", version)
fmt.Printf("build: %s\n", build)
}
19 changes: 19 additions & 0 deletions cmd/interactive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmd

import (
"fmt"
"os"

"github.com/hussar-lang/hussar/repl"

"github.com/spf13/cobra"
)

var interactive = &cobra.Command{
Use: "interactive",
Short: "Start the interactive shell",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Starting Hussar interactive interpreter.")
repl.Start(os.Stdin, os.Stdout)
},
}
74 changes: 74 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cmd

import (
"fmt"

log "github.com/sirupsen/logrus"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var (
version string
build string
)

var rootCmd = &cobra.Command{
Use: "hussar",
}

// Setup populates the version and build fields
func Setup(versionStr string, buildStr string) {
version = versionStr
build = buildStr
rootCmd.Short = fmt.Sprintf("The Hussar programming language - %s (build %s)", version, build)
rootCmd.SetVersionTemplate(fmt.Sprintf("%s (build %s)", version, build))
}

// Execute executes the commands
func Execute() {
rootCmd.AddCommand(run, interactive, env)
if err := rootCmd.Execute(); err != nil {
log.WithError(err).Fatal()
}
}

func init() {
cobra.OnInitialize(initialize)

// Global flags
rootCmd.PersistentFlags().String("log.level", "warn", "one of debug, info, warn, error or fatal")
rootCmd.PersistentFlags().String("log.format", "text", "one of text or json")

// Flag binding
viper.BindPFlags(rootCmd.PersistentFlags())
}

func initialize() {
// Environment variables
viper.SetEnvPrefix("hussar")
viper.AutomaticEnv()

// Configuration file
viper.SetConfigName("hs-config")
viper.AddConfigPath(".")
viper.AddConfigPath("$HOME/.hussar/")
if err := viper.ReadInConfig(); err != nil {
log.Info("No valid configuration file found")
}
lvl := viper.GetString("log.level")
l, err := log.ParseLevel(lvl)
if err != nil {
log.WithField("level", lvl).Warn("Invalid log level, fallback to 'warn'")
} else {
log.SetLevel(l)
}
switch viper.GetString("log.format") {
case "json":
log.SetFormatter(&log.JSONFormatter{})
default:
case "text":
log.SetFormatter(&log.TextFormatter{})
}
}
109 changes: 109 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package cmd

import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/ttacon/chalk"

"github.com/hussar-lang/hussar/evaluator"
"github.com/hussar-lang/hussar/lexer"
"github.com/hussar-lang/hussar/object"
"github.com/hussar-lang/hussar/parser"
)

var run = &cobra.Command{
Use: "run",
Short: "Run the given script",
Run: func(cmd *cobra.Command, args []string) {
runFromFile()
},
}

func init() {
run.Flags().String("source.file", "", "the source file to run")
viper.BindPFlags(run.Flags())
}

func runFromFile() {
file, err := getSourceFile(viper.GetString("source.file"))
if err != nil {
log.Fatal(err)
}

l := lexer.New(string(file))
p := parser.New(l)
program := p.ParseProgram()

if len(p.Errors()) != 0 {
printParserErrors(os.Stderr, p.Errors())
os.Exit(1)
}

env := object.NewEnvironment()
eval := evaluator.Eval(program, env)
if eval.Inspect() != "NULL" {
fmt.Println(eval.Inspect())
}
}

func getSourceFile(sourceFile string) ([]byte, error) {
if sourceFile == "" {
cwd, err := os.Getwd()
if err != nil {
log.WithError(err).Fatal()
}

var files []string
filepath.Walk(cwd, func(path string, f os.FileInfo, _ error) error {
if !f.IsDir() {
if filepath.Ext(path) == "hss" {
files = append(files, f.Name())
}
}
return nil
})

if len(files) == 0 {
log.WithError(errors.New("no source files found in current directory")).Fatal()
} else if len(files) > 1 {
for _, f := range files {
if strings.ToLower(f) == "main.hss" {
sourceFile = f
break
}
}

// Hack, I know
if sourceFile == "" {
log.WithError(errors.New("no main source file found in current directory - specify one with the source.file flag")).Fatal()
}
} else {
sourceFile = files[0]
}
}

source, err := ioutil.ReadFile(sourceFile)
if err != nil {
log.Fatal(err)
}

return source, nil
}

func printParserErrors(out io.Writer, errors []string) {
errColor := chalk.Red.NewStyle().WithTextStyle(chalk.Bold).Style

io.WriteString(out, errColor("PARSER ERROR!\n"))
for _, msg := range errors {
io.WriteString(out, errColor(" [!] ")+msg+"\n")
}
}
87 changes: 3 additions & 84 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,96 +1,15 @@
package main

import (
"fmt"
"io"
"io/ioutil"
"os"

"github.com/hussar-lang/hussar/evaluator"
"github.com/hussar-lang/hussar/lexer"
"github.com/hussar-lang/hussar/object"
"github.com/hussar-lang/hussar/parser"
"github.com/hussar-lang/hussar/repl"

log "github.com/sirupsen/logrus"
"github.com/ttacon/chalk"
"gopkg.in/alecthomas/kingpin.v2"
"github.com/hussar-lang/hussar/cmd"
)

var (
GitCommit string
VersionString string

app = kingpin.New("hussar", "The Hussar interpreter")
verbose = app.Flag("verbose", "Enable verbose logging.").Short('v').Bool()

// TODO: run interactive mode if no subcommand was given (see #1)
interactive = app.Command("interactive", "Interactive REPL")

run = app.Command("run", "Run Hussar code")
runFile = run.Flag("file", "Code to run").Required().Short('f').ExistingFile()
)

func init() {
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
log.SetOutput(os.Stdout)
}

func main() {
app.Version(fmt.Sprintf("%s (%s)", VersionString, GitCommit))
args, err := app.Parse(os.Args[1:])

if *verbose {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.WarnLevel)
}

switch kingpin.MustParse(args, err) {
case run.FullCommand():
log.WithFields(log.Fields{
"File": *runFile,
"Verbose": *verbose,
}).Debug("Received run command")

runFromFile()
case interactive.FullCommand():
startRepl()
}
}

func runFromFile() {
file, err := ioutil.ReadFile(*runFile)
if err != nil {
log.Fatal(err)
}

l := lexer.New(string(file))
p := parser.New(l)
program := p.ParseProgram()

if len(p.Errors()) != 0 {
printParserErrors(os.Stdout, p.Errors())
os.Exit(21)
}

env := object.NewEnvironment()
eval := evaluator.Eval(program, env)
if eval.Inspect() != "NULL" {
fmt.Println(eval.Inspect())
}
}

func startRepl() {
fmt.Printf("Starting Hussar interactive interpreter v%s\n", VersionString)
repl.Start(os.Stdin, os.Stdout)
}

func printParserErrors(out io.Writer, errors []string) {
errColor := chalk.Red.NewStyle().WithTextStyle(chalk.Bold).Style

io.WriteString(out, errColor("PARSER ERROR!\n"))
for _, msg := range errors {
io.WriteString(out, errColor(" [!] ")+msg+"\n")
}
cmd.Setup(VersionString, GitCommit)
cmd.Execute()
}

0 comments on commit 2c1769e

Please sign in to comment.