Compare commits

...

18 Commits

Author SHA1 Message Date
b94fb3b31e Remaining flow control in RBE 2023-09-30 19:51:09 -04:00
dacbddca6e Some if-let in RBE 2023-09-30 19:39:55 -04:00
6e50ec65f8 Flow control match binding in RBE 2023-09-29 16:52:18 -04:00
ca812add16 Much more flow control destructuring in RBE 2023-09-27 23:16:10 -04:00
460741740b Flow control for in RBE 2023-09-26 11:03:55 -04:00
7204f08177 Expressions and starting flow control in RBE 2023-09-26 06:43:22 -04:00
505e04613f Up through conversion in RBE 2023-09-26 06:07:51 -04:00
7f4f627769 Much renaming and refreshing of RBE 2023-09-24 20:46:41 -04:00
dc8045a20e Trivial goop while refreshing on RBE 2023-09-24 10:57:16 -04:00
41f53c34c0 Making some arduino-shaped messes 2023-01-07 18:44:31 -05:00
6c0083e094 Sudoku through the end of chapter 8 2022-11-26 16:09:32 -05:00
7f2f579241 Sudoku up through chapter 7 2022-11-26 09:42:41 -05:00
7d2ac20ce1 Beginning of sudoku chapter 7 with SDM loading 2022-11-25 15:37:57 -05:00
8f38a3b271 Sudoku up through chapter 5 2022-11-25 12:20:01 -05:00
96f5f85a27 Piston Sudoku up through 3rd chapter 2022-11-24 18:56:18 -05:00
6cf80e09b1 Piston tutorial getting started woop! 2022-11-24 18:25:07 -05:00
30ed115ed0 Moving argh to separate repo 2022-06-06 18:50:25 -04:00
8376608a1e Merge pull request 'arghing' (#1) from arghing into main
Reviewed-on: #1
2022-06-06 22:44:24 +00:00
95 changed files with 5054 additions and 2258 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@
**/target/
/hello_world/main
/aoc*/**/input
/arduino/build-*/

2
arduino/.envrc Normal file
View File

@@ -0,0 +1,2 @@
export ARDMK_DIR=/usr/share/arduino
export ARDMK_VENDOR=archlinux-arduino

3
arduino/Makefile Normal file
View File

@@ -0,0 +1,3 @@
BOARD_TAG = uno
include $(ARDMK_DIR)/Arduino.mk

31
arduino/sos.ino Normal file
View File

@@ -0,0 +1,31 @@
#define DIT_DURATION_MS 88
#define LETTER_PAUSE_MS 1000
void setup() {
pinMode(13, OUTPUT);
}
void dit() {
digitalWrite(13, HIGH);
delay(DIT_DURATION_MS);
digitalWrite(13, LOW);
delay(DIT_DURATION_MS);
}
void dah() {
digitalWrite(13, HIGH);
delay(DIT_DURATION_MS * 3);
digitalWrite(13, LOW);
delay(DIT_DURATION_MS);
}
void loop() {
dit(); dit(); dit();
delay(LETTER_PAUSE_MS);
dah();
delay(LETTER_PAUSE_MS);
dit(); dit(); dit();
delay(LETTER_PAUSE_MS);
}

View File

@@ -1,4 +0,0 @@
# argh command line parser
> NOTE: much of this is lifted from
> https://blog.gopheracademy.com/advent-2014/parsers-lexers/

View File

@@ -1,34 +0,0 @@
package argh
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
tracingEnabled = os.Getenv("ARGH_TRACING") == "enabled"
traceLogger *log.Logger
)
func init() {
if !tracingEnabled {
return
}
traceLogger = log.New(os.Stderr, "ARGH TRACING: ", 0)
}
func tracef(format string, v ...any) {
if !tracingEnabled {
return
}
if _, file, line, ok := runtime.Caller(1); ok {
format = fmt.Sprintf("%v:%v ", filepath.Base(file), line) + format
}
traceLogger.Printf(format, v...)
}

View File

@@ -1,46 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"git.meatballhat.com/x/box-o-sand/argh"
"github.com/davecgh/go-spew/spew"
)
func main() {
asJSON := os.Getenv("ARGH_OUTPUT_JSON") == "enabled"
log.SetFlags(0)
pt, err := argh.ParseArgs(os.Args, argh.NewParserConfig(
&argh.CommandConfig{
NValue: argh.OneOrMoreValue,
ValueNames: []string{"topping"},
Flags: &argh.Flags{
Automatic: true,
},
},
nil,
))
if err != nil {
log.Fatal(err)
}
ast := argh.NewQuerier(pt.Nodes).AST()
if asJSON {
b, err := json.MarshalIndent(ast, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
return
}
spew.Dump(ast)
}

View File

@@ -1,12 +0,0 @@
module git.meatballhat.com/x/box-o-sand/argh
go 1.18
require github.com/pkg/errors v0.9.1
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.7.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

View File

@@ -1,12 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,46 +0,0 @@
package argh
type Node interface{}
type TypedNode struct {
Type string
Node Node
}
type PassthroughArgs struct {
Nodes []Node
}
type CompoundShortFlag struct {
Nodes []Node
}
type Ident struct {
Literal string
}
type BadArg struct {
Literal string
From Pos
To Pos
}
type Command struct {
Name string
Values map[string]string
Nodes []Node
}
type Flag struct {
Name string
Values map[string]string
Nodes []Node
}
type StdinFlag struct{}
type StopFlag struct{}
type ArgDelimiter struct{}
type Assign struct{}

View File

@@ -1,26 +0,0 @@
// Code generated by "stringer -type NValue"; DO NOT EDIT.
package argh
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[OneOrMoreValue - -2]
_ = x[ZeroOrMoreValue - -1]
_ = x[ZeroValue-0]
}
const _NValue_name = "OneOrMoreValueZeroOrMoreValueZeroValue"
var _NValue_index = [...]uint8{0, 14, 29, 38}
func (i NValue) String() string {
i -= -2
if i < 0 || i >= NValue(len(_NValue_index)-1) {
return "NValue(" + strconv.FormatInt(int64(i+-2), 10) + ")"
}
return _NValue_name[_NValue_index[i]:_NValue_index[i+1]]
}

View File

@@ -1,346 +0,0 @@
package argh
import (
"fmt"
"io"
"strings"
)
type parser struct {
s *Scanner
cfg *ParserConfig
errors ParserErrorList
tok Token
lit string
pos Pos
buffered bool
}
type ParseTree struct {
Nodes []Node `json:"nodes"`
}
func ParseArgs(args []string, pCfg *ParserConfig) (*ParseTree, error) {
p := &parser{}
p.init(
strings.NewReader(strings.Join(args, string(nul))),
pCfg,
)
tracef("ParseArgs(...) parser=%+#v", p)
return p.parseArgs()
}
func (p *parser) addError(msg string) {
p.errors.Add(Position{Column: int(p.pos)}, msg)
}
func (p *parser) init(r io.Reader, pCfg *ParserConfig) {
p.errors = ParserErrorList{}
if pCfg == nil {
pCfg = POSIXyParserConfig
}
p.cfg = pCfg
p.s = NewScanner(r, pCfg.ScannerConfig)
p.next()
}
func (p *parser) parseArgs() (*ParseTree, error) {
if p.errors.Len() != 0 {
tracef("parseArgs() bailing due to initial error")
return nil, p.errors.Err()
}
tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog)
prog := p.parseCommand(&p.cfg.Prog)
tracef("parseArgs() top level node is %T", prog)
nodes := []Node{prog}
if v := p.parsePassthrough(); v != nil {
tracef("parseArgs() appending passthrough argument %v", v)
nodes = append(nodes, v)
}
tracef("parseArgs() returning ParseTree")
return &ParseTree{Nodes: nodes}, p.errors.Err()
}
func (p *parser) next() {
tracef("next() before scan: %v %q %v", p.tok, p.lit, p.pos)
p.tok, p.lit, p.pos = p.s.Scan()
tracef("next() after scan: %v %q %v", p.tok, p.lit, p.pos)
}
func (p *parser) parseCommand(cCfg *CommandConfig) Node {
tracef("parseCommand(%+#v)", cCfg)
node := &Command{
Name: p.lit,
}
values := map[string]string{}
nodes := []Node{}
identIndex := 0
for i := 0; p.tok != EOL; i++ {
if !p.buffered {
tracef("parseCommand(...) buffered=false; scanning next")
p.next()
}
p.buffered = false
tracef("parseCommand(...) for=%d values=%+#v", i, values)
tracef("parseCommand(...) for=%d nodes=%+#v", i, nodes)
tracef("parseCommand(...) for=%d tok=%s lit=%q pos=%v", i, p.tok, p.lit, p.pos)
tracef("parseCommand(...) cCfg=%+#v", cCfg)
if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok {
subCommand := p.lit
nodes = append(nodes, p.parseCommand(&subCfg))
tracef("parseCommand(...) breaking after sub-command=%v", subCommand)
break
}
switch p.tok {
case ARG_DELIMITER:
tracef("parseCommand(...) handling %s", p.tok)
nodes = append(nodes, &ArgDelimiter{})
continue
case IDENT, STDIN_FLAG:
tracef("parseCommand(...) handling %s", p.tok)
if cCfg.NValue.Contains(identIndex) {
name := fmt.Sprintf("%d", identIndex)
tracef("parseCommand(...) checking for name of identIndex=%d", identIndex)
if len(cCfg.ValueNames) > identIndex {
name = cCfg.ValueNames[identIndex]
tracef("parseCommand(...) setting name=%s from config value names", name)
} else if len(cCfg.ValueNames) == 1 && (cCfg.NValue == OneOrMoreValue || cCfg.NValue == ZeroOrMoreValue) {
name = fmt.Sprintf("%s.%d", cCfg.ValueNames[0], identIndex)
tracef("parseCommand(...) setting name=%s from repeating value name", name)
}
values[name] = p.lit
}
if p.tok == STDIN_FLAG {
nodes = append(nodes, &StdinFlag{})
} else {
nodes = append(nodes, &Ident{Literal: p.lit})
}
identIndex++
case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG:
tok := p.tok
flagNode := p.parseFlag(cCfg.Flags)
tracef("parseCommand(...) appending %s node=%+#v", tok, flagNode)
nodes = append(nodes, flagNode)
case ASSIGN:
tracef("parseCommand(...) error on bare %s", p.tok)
p.addError("invalid bare assignment")
break
default:
tracef("parseCommand(...) breaking on %s", p.tok)
break
}
}
if len(nodes) > 0 {
node.Nodes = nodes
}
if len(values) > 0 {
node.Values = values
}
tracef("parseCommand(...) returning node=%+#v", node)
return node
}
func (p *parser) parseIdent() Node {
node := &Ident{Literal: p.lit}
return node
}
func (p *parser) parseFlag(flags *Flags) Node {
switch p.tok {
case SHORT_FLAG:
tracef("parseFlag(...) parsing short flag with config=%+#v", flags)
return p.parseShortFlag(flags)
case LONG_FLAG:
tracef("parseFlag(...) parsing long flag with config=%+#v", flags)
return p.parseLongFlag(flags)
case COMPOUND_SHORT_FLAG:
tracef("parseFlag(...) parsing compound short flag with config=%+#v", flags)
return p.parseCompoundShortFlag(flags)
}
panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok))
}
func (p *parser) parseShortFlag(flags *Flags) Node {
node := &Flag{Name: string(p.lit[1])}
flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
return node
}
return p.parseConfiguredFlag(node, flCfg)
}
func (p *parser) parseLongFlag(flags *Flags) Node {
node := &Flag{Name: string(p.lit[2:])}
flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
return node
}
return p.parseConfiguredFlag(node, flCfg)
}
func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
flagNodes := []Node{}
withoutFlagPrefix := p.lit[1:]
for i, r := range withoutFlagPrefix {
node := &Flag{Name: string(r)}
if i == len(withoutFlagPrefix)-1 {
flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
continue
}
flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg))
continue
}
flagNodes = append(flagNodes, node)
}
return &CompoundShortFlag{Nodes: flagNodes}
}
func (p *parser) parseConfiguredFlag(node *Flag, flCfg FlagConfig) Node {
values := map[string]string{}
nodes := []Node{}
identIndex := 0
for i := 0; p.tok != EOL; i++ {
if !flCfg.NValue.Contains(identIndex) {
tracef("parseConfiguredFlag(...) identIndex=%d exceeds expected=%v; breaking", identIndex, flCfg.NValue)
break
}
p.next()
switch p.tok {
case ARG_DELIMITER:
nodes = append(nodes, &ArgDelimiter{})
continue
case ASSIGN:
nodes = append(nodes, &Assign{})
continue
case IDENT, STDIN_FLAG:
name := fmt.Sprintf("%d", identIndex)
tracef("parseConfiguredFlag(...) checking for name of identIndex=%d", identIndex)
if len(flCfg.ValueNames) > identIndex {
name = flCfg.ValueNames[identIndex]
tracef("parseConfiguredFlag(...) setting name=%s from config value names", name)
} else if len(flCfg.ValueNames) == 1 && (flCfg.NValue == OneOrMoreValue || flCfg.NValue == ZeroOrMoreValue) {
name = fmt.Sprintf("%s.%d", flCfg.ValueNames[0], identIndex)
tracef("parseConfiguredFlag(...) setting name=%s from repeating value name", name)
} else {
tracef("parseConfiguredFlag(...) setting name=%s", name)
}
values[name] = p.lit
if p.tok == STDIN_FLAG {
nodes = append(nodes, &StdinFlag{})
} else {
nodes = append(nodes, &Ident{Literal: p.lit})
}
identIndex++
default:
tracef("parseConfiguredFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos)
p.buffered = true
if len(nodes) > 0 {
node.Nodes = nodes
}
if len(values) > 0 {
node.Values = values
}
return node
}
}
if len(nodes) > 0 {
node.Nodes = nodes
}
if len(values) > 0 {
node.Values = values
}
return node
}
func (p *parser) parsePassthrough() Node {
nodes := []Node{}
for ; p.tok != EOL; p.next() {
nodes = append(nodes, &Ident{Literal: p.lit})
}
if len(nodes) == 0 {
return nil
}
return &PassthroughArgs{Nodes: nodes}
}

View File

@@ -1,146 +0,0 @@
package argh
const (
OneOrMoreValue NValue = -2
ZeroOrMoreValue NValue = -1
ZeroValue NValue = 0
)
var (
POSIXyParserConfig = NewParserConfig(
nil,
POSIXyScannerConfig,
)
)
type NValue int
func (nv NValue) Contains(i int) bool {
tracef("NValue.Contains(%v)", i)
if i < int(ZeroValue) {
return false
}
if nv == OneOrMoreValue || nv == ZeroOrMoreValue {
return true
}
return int(nv) > i
}
type ParserConfig struct {
Prog CommandConfig
ScannerConfig *ScannerConfig
}
func NewParserConfig(prog *CommandConfig, sCfg *ScannerConfig) *ParserConfig {
if sCfg == nil {
sCfg = POSIXyScannerConfig
}
if prog == nil {
prog = &CommandConfig{}
}
prog.init()
pCfg := &ParserConfig{
Prog: *prog,
ScannerConfig: sCfg,
}
return pCfg
}
type CommandConfig struct {
NValue NValue
ValueNames []string
Flags *Flags
Commands *Commands
}
func (cCfg *CommandConfig) init() {
if cCfg.ValueNames == nil {
cCfg.ValueNames = []string{}
}
if cCfg.Flags == nil {
cCfg.Flags = &Flags{}
}
if cCfg.Commands == nil {
cCfg.Commands = &Commands{}
}
}
func (cCfg *CommandConfig) GetCommandConfig(name string) (CommandConfig, bool) {
tracef("CommandConfig.GetCommandConfig(%q)", name)
if cCfg.Commands == nil {
cCfg.Commands = &Commands{Map: map[string]CommandConfig{}}
}
return cCfg.Commands.Get(name)
}
func (cCfg *CommandConfig) GetFlagConfig(name string) (FlagConfig, bool) {
tracef("CommandConfig.GetFlagConfig(%q)", name)
if cCfg.Flags == nil {
cCfg.Flags = &Flags{Map: map[string]FlagConfig{}}
}
return cCfg.Flags.Get(name)
}
type FlagConfig struct {
NValue NValue
Persist bool
ValueNames []string
}
type Flags struct {
Parent *Flags
Map map[string]FlagConfig
Automatic bool
}
func (fl *Flags) Get(name string) (FlagConfig, bool) {
tracef("Flags.Get(%q)", name)
if fl.Map == nil {
fl.Map = map[string]FlagConfig{}
}
flCfg, ok := fl.Map[name]
if !ok {
if fl.Automatic {
return FlagConfig{}, true
}
if fl.Parent != nil {
flCfg, ok = fl.Parent.Get(name)
return flCfg, ok && flCfg.Persist
}
}
return flCfg, ok
}
type Commands struct {
Map map[string]CommandConfig
}
func (cmd *Commands) Get(name string) (CommandConfig, bool) {
tracef("Commands.Get(%q)", name)
if cmd.Map == nil {
cmd.Map = map[string]CommandConfig{}
}
cmdCfg, ok := cmd.Map[name]
return cmdCfg, ok
}

View File

@@ -1,88 +0,0 @@
package argh
import (
"fmt"
"io"
"sort"
)
// ParserError is largely borrowed from go/scanner.Error
type ParserError struct {
Pos Position
Msg string
}
func (e ParserError) Error() string {
if e.Pos.IsValid() {
return e.Pos.String() + ":" + e.Msg
}
return e.Msg
}
// ParserErrorList is largely borrowed from go/scanner.ErrorList
type ParserErrorList []*ParserError
func (el *ParserErrorList) Add(pos Position, msg string) {
*el = append(*el, &ParserError{Pos: pos, Msg: msg})
}
func (el *ParserErrorList) Reset() { *el = (*el)[0:0] }
func (el ParserErrorList) Len() int { return len(el) }
func (el ParserErrorList) Swap(i, j int) { el[i], el[j] = el[j], el[i] }
func (el ParserErrorList) Less(i, j int) bool {
e := &el[i].Pos
f := &el[j].Pos
if e.Column != f.Column {
return e.Column < f.Column
}
return el[i].Msg < el[j].Msg
}
func (el ParserErrorList) Sort() {
sort.Sort(el)
}
func (el ParserErrorList) Error() string {
switch len(el) {
case 0:
return "no errors"
case 1:
return el[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", el[0], len(el)-1)
}
func (el ParserErrorList) Err() error {
if len(el) == 0 {
return nil
}
return el
}
func (el ParserErrorList) Is(other error) bool {
if _, ok := other.(ParserErrorList); ok {
return el.Error() == other.Error()
}
if v, ok := other.(*ParserErrorList); ok {
return el.Error() == (*v).Error()
}
return false
}
func PrintParserError(w io.Writer, err error) {
if list, ok := err.(ParserErrorList); ok {
for _, e := range list {
fmt.Fprintf(w, "%s\n", e)
}
} else if err != nil {
fmt.Fprintf(w, "%s\n", err)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,94 +0,0 @@
package argh
type Querier interface {
Program() (*Command, bool)
AST() []Node
}
func NewQuerier(nodes []Node) Querier {
return &defaultQuerier{nodes: nodes}
}
type defaultQuerier struct {
nodes []Node
}
func (dq *defaultQuerier) Program() (*Command, bool) {
if len(dq.nodes) == 0 {
tracef("Program nodes are empty")
return nil, false
}
tracef("Program node[0] is %T", dq.nodes[0])
v, ok := dq.nodes[0].(*Command)
if ok && v.Name == "" {
return v, false
}
return v, ok
}
func (dq *defaultQuerier) AST() []Node {
ret := []Node{}
for i, node := range dq.nodes {
tracef("AST i=%d node type=%T", i, node)
if _, ok := node.(*ArgDelimiter); ok {
continue
}
if _, ok := node.(*StopFlag); ok {
continue
}
if v, ok := node.(*CompoundShortFlag); ok {
if v.Nodes != nil {
ret = append(ret, NewQuerier(v.Nodes).AST()...)
}
continue
}
if v, ok := node.(*Command); ok {
astNodes := NewQuerier(v.Nodes).AST()
if len(astNodes) == 0 {
astNodes = nil
}
ret = append(
ret,
&Command{
Name: v.Name,
Values: v.Values,
Nodes: astNodes,
})
continue
}
if v, ok := node.(*Flag); ok {
astNodes := NewQuerier(v.Nodes).AST()
if len(astNodes) == 0 {
astNodes = nil
}
ret = append(
ret,
&Flag{
Name: v.Name,
Values: v.Values,
Nodes: astNodes,
})
continue
}
ret = append(ret, node)
}
return ret
}

View File

@@ -1,60 +0,0 @@
package argh_test
import (
"testing"
"git.meatballhat.com/x/box-o-sand/argh"
"github.com/stretchr/testify/require"
)
func TestQuerier_Program(t *testing.T) {
for _, tc := range []struct {
name string
args []string
cfg *argh.ParserConfig
exp string
expOK bool
}{
{
name: "typical",
args: []string{"pizzas", "ahoy", "--treatsa", "fun"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: &argh.Commands{
Map: map[string]argh.CommandConfig{
"ahoy": argh.CommandConfig{
Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"treatsa": argh.FlagConfig{NValue: 1},
},
},
},
},
},
},
},
exp: "pizzas",
expOK: true,
},
{
name: "minimal",
args: []string{"pizzas"},
exp: "pizzas",
expOK: true,
},
{
name: "invalid",
args: []string{},
expOK: false,
},
} {
t.Run(tc.name, func(ct *testing.T) {
pt, err := argh.ParseArgs(tc.args, tc.cfg)
require.Nil(ct, err)
prog, ok := argh.NewQuerier(pt.Nodes).Program()
require.Equal(ct, tc.expOK, ok)
require.Equal(ct, tc.exp, prog.Name)
})
}
}

View File

@@ -1,159 +0,0 @@
package argh
import (
"bufio"
"bytes"
"errors"
"io"
"log"
"unicode"
)
type Scanner struct {
r *bufio.Reader
i int
cfg *ScannerConfig
}
func NewScanner(r io.Reader, cfg *ScannerConfig) *Scanner {
if cfg == nil {
cfg = POSIXyScannerConfig
}
return &Scanner{
r: bufio.NewReader(r),
cfg: cfg,
}
}
func (s *Scanner) Scan() (Token, string, Pos) {
ch, pos := s.read()
if s.cfg.IsBlankspace(ch) {
_ = s.unread()
return s.scanBlankspace()
}
if s.cfg.IsAssignmentOperator(ch) {
return ASSIGN, string(ch), pos
}
if s.cfg.IsMultiValueDelim(ch) {
return MULTI_VALUE_DELIMITER, string(ch), pos
}
if ch == eol {
return EOL, "", pos
}
if ch == nul {
return ARG_DELIMITER, string(ch), pos
}
if unicode.IsGraphic(ch) {
_ = s.unread()
return s.scanArg()
}
return ILLEGAL, string(ch), pos
}
func (s *Scanner) read() (rune, Pos) {
ch, _, err := s.r.ReadRune()
s.i++
if errors.Is(err, io.EOF) {
return eol, Pos(s.i)
} else if err != nil {
log.Printf("unknown scanner error=%+v", err)
return eol, Pos(s.i)
}
return ch, Pos(s.i)
}
func (s *Scanner) unread() Pos {
_ = s.r.UnreadRune()
s.i--
return Pos(s.i)
}
func (s *Scanner) scanBlankspace() (Token, string, Pos) {
buf := &bytes.Buffer{}
ch, pos := s.read()
buf.WriteRune(ch)
for {
ch, pos = s.read()
if ch == eol {
break
} else if !s.cfg.IsBlankspace(ch) {
pos = s.unread()
break
} else {
_, _ = buf.WriteRune(ch)
}
}
return BS, buf.String(), pos
}
func (s *Scanner) scanArg() (Token, string, Pos) {
buf := &bytes.Buffer{}
ch, pos := s.read()
buf.WriteRune(ch)
for {
ch, pos = s.read()
if ch == eol || ch == nul || s.cfg.IsAssignmentOperator(ch) || s.cfg.IsMultiValueDelim(ch) {
pos = s.unread()
break
}
_, _ = buf.WriteRune(ch)
}
str := buf.String()
if len(str) == 0 {
return EMPTY, str, pos
}
ch0 := rune(str[0])
if len(str) == 1 {
if s.cfg.IsFlagPrefix(ch0) {
return STDIN_FLAG, str, pos
}
if s.cfg.IsAssignmentOperator(ch0) {
return ASSIGN, str, pos
}
return IDENT, str, pos
}
ch1 := rune(str[1])
if len(str) == 2 {
if s.cfg.IsFlagPrefix(ch0) && s.cfg.IsFlagPrefix(ch1) {
return STOP_FLAG, str, pos
}
if s.cfg.IsFlagPrefix(ch0) {
return SHORT_FLAG, str, pos
}
}
if s.cfg.IsFlagPrefix(ch0) {
if s.cfg.IsFlagPrefix(ch1) {
return LONG_FLAG, str, pos
}
return COMPOUND_SHORT_FLAG, str, pos
}
return IDENT, str, pos
}

View File

@@ -1,39 +0,0 @@
package argh
var (
// POSIXyScannerConfig defines a scanner config that uses '-'
// as the flag prefix, which also means that "--" is the "long
// flag" prefix, a bare "--" is considered STOP_FLAG, and a
// bare "-" is considered STDIN_FLAG.
POSIXyScannerConfig = &ScannerConfig{
AssignmentOperator: '=',
FlagPrefix: '-',
MultiValueDelim: ',',
}
)
type ScannerConfig struct {
AssignmentOperator rune
FlagPrefix rune
MultiValueDelim rune
}
func (cfg *ScannerConfig) IsFlagPrefix(ch rune) bool {
return ch == cfg.FlagPrefix
}
func (cfg *ScannerConfig) IsMultiValueDelim(ch rune) bool {
return ch == cfg.MultiValueDelim
}
func (cfg *ScannerConfig) IsAssignmentOperator(ch rune) bool {
return ch == cfg.AssignmentOperator
}
func (cfg *ScannerConfig) IsBlankspace(ch rune) bool {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
}
func (cfg *ScannerConfig) IsUnderscore(ch rune) bool {
return ch == '_'
}

View File

@@ -1,53 +0,0 @@
//go:generate stringer -type Token
package argh
import "fmt"
const (
ILLEGAL Token = iota
EOL
EMPTY // ''
BS // ' ' '\t' '\n'
IDENT // char group without flag prefix: 'some' 'words'
ARG_DELIMITER // rune(0)
ASSIGN // '='
MULTI_VALUE_DELIMITER // ','
LONG_FLAG // char group with double flag prefix: '--flag'
SHORT_FLAG // single char with single flag prefix: '-f'
COMPOUND_SHORT_FLAG // char group with single flag prefix: '-flag'
STDIN_FLAG // '-'
STOP_FLAG // '--'
nul = rune(0)
eol = rune(-1)
)
type Token int
// Position is adapted from go/token.Position
type Position struct {
Column int
}
func (p *Position) IsValid() bool { return p.Column > 0 }
func (p Position) String() string {
s := ""
if p.IsValid() {
s = fmt.Sprintf("%d", p.Column)
}
if s == "" {
s = "-"
}
return s
}
// Pos is borrowed from go/token.Pos
type Pos int
const NoPos Pos = 0
func (p Pos) IsValid() bool {
return p != NoPos
}

View File

@@ -1,35 +0,0 @@
// Code generated by "stringer -type Token"; DO NOT EDIT.
package argh
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ILLEGAL-0]
_ = x[EOL-1]
_ = x[EMPTY-2]
_ = x[BS-3]
_ = x[IDENT-4]
_ = x[ARG_DELIMITER-5]
_ = x[ASSIGN-6]
_ = x[MULTI_VALUE_DELIMITER-7]
_ = x[LONG_FLAG-8]
_ = x[SHORT_FLAG-9]
_ = x[COMPOUND_SHORT_FLAG-10]
_ = x[STDIN_FLAG-11]
_ = x[STOP_FLAG-12]
}
const _Token_name = "ILLEGALEOLEMPTYBSIDENTARG_DELIMITERASSIGNMULTI_VALUE_DELIMITERLONG_FLAGSHORT_FLAGCOMPOUND_SHORT_FLAGSTDIN_FLAGSTOP_FLAG"
var _Token_index = [...]uint8{0, 7, 10, 15, 17, 22, 35, 41, 62, 71, 81, 100, 110, 119}
func (i Token) String() string {
if i < 0 || i >= Token(len(_Token_index)-1) {
return "Token(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Token_name[_Token_index[i]:_Token_index[i+1]]
}

1876
piston-tutorials/getting-started/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
[package]
name = "spinning-square"
version = "0.1.0"
authors = [
"Dan Buch <dan@meatballhat.com>",
"TyOverby <ty@pre-alpha.com>",
"Nikita Pekin <contact@nikitapek.in>"
]
[[bin]]
name = "spinning-square"
[dependencies]
piston = "0.53.0"
piston2d-graphics = "0.43.0"
pistoncore-glutin_window = "0.70.1"
piston2d-opengl_graphics = "0.82.0"

View File

@@ -0,0 +1,70 @@
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventSettings, Events};
use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent};
use piston::window::WindowSettings;
pub struct App {
gl: GlGraphics,
rotation: f64,
}
impl App {
fn render(&mut self, args: &RenderArgs) {
use graphics::*;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
let square = rectangle::square(0.0, 0.0, 50.0);
let rotation = self.rotation;
let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0);
self.gl.draw(args.viewport(), |c, gl| {
clear(GREEN, gl);
let transform = c
.transform
.trans(x, y)
.rot_rad(rotation)
.trans(-25.0, -25.0);
rectangle(RED, square, transform, gl);
})
}
fn update(&mut self, args: &UpdateArgs) {
self.rotation += 2.0 * args.dt;
}
}
fn main() {
let opengl = OpenGL::V3_2;
let mut window: Window = WindowSettings::new("spinning-square", [200, 200])
.graphics_api(opengl)
.exit_on_esc(true)
.build()
.unwrap();
let mut app = App {
gl: GlGraphics::new(opengl),
rotation: 0.0,
};
let mut events = Events::new(EventSettings::new());
while let Some(e) = events.next(&mut window) {
if let Some(args) = e.render_args() {
app.render(&args);
}
if let Some(args) = e.update_args() {
app.update(&args);
}
}
}

1876
piston-tutorials/sudoku/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
[package]
name = "sudoku"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
piston = "0.53.2"
piston2d-graphics = "0.43.0"
piston2d-opengl_graphics = "0.82.0"
pistoncore-glutin_window = "0.70.1"

Binary file not shown.

View File

@@ -0,0 +1,99 @@
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Mono.
Copyright (c) 2014, Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,155 @@
//! Game board logic.
use std::fs::read_to_string;
const SIZE: usize = 9;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Cell {
pub value: u8,
pub loaded: bool,
pub invalid: bool,
}
#[derive(Debug, PartialEq)]
pub struct Gameboard {
pub cells: [[Cell; SIZE]; SIZE],
pub completed: bool,
}
impl Gameboard {
pub fn new() -> Gameboard {
Gameboard {
cells: [[Cell::default(); SIZE]; SIZE],
completed: false,
}
}
pub fn from_cells(cells: [[u8; SIZE]; SIZE]) -> Gameboard {
let mut ret = Gameboard::new();
for (i, row) in cells.iter().enumerate() {
for (j, &col) in row.iter().enumerate() {
ret.cells[i][j] = Cell {
value: col,
loaded: col != 0,
invalid: false,
};
}
}
ret
}
pub fn char(&self, ind: [usize; 2]) -> Option<char> {
Some(match self.cells[ind[1]][ind[0]].value {
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
7 => '7',
8 => '8',
9 => '9',
_ => return None,
})
}
pub fn set(&mut self, ind: [usize; 2], val: u8) {
if !self.cells[ind[1]][ind[0]].loaded {
self.validate(ind, val);
self.cells[ind[1]][ind[0]].value = val;
}
self.completed = self
.cells
.iter()
.flatten()
.all(|cell| !cell.invalid && cell.value != 0);
}
pub fn load_sdm(filename: &str) -> Self {
let data = read_to_string(filename).expect("failed to read SDM file");
let mut cells = [[Cell::default(); SIZE]; SIZE];
let mut row = 0;
let mut col = 0;
for c in data.chars() {
if col == SIZE {
col = 0;
row += 1;
}
if let Some(v) = c.to_digit(10) {
let value = v as u8;
cells[row][col] = Cell {
value,
loaded: value != 0,
invalid: false,
};
col += 1;
}
}
Self {
cells,
completed: false,
}
}
fn validate(&mut self, ind: [usize; 2], val: u8) {
let [b, a] = ind;
for i in 0..SIZE {
if i == a {
continue;
}
if self.cells[a][i].value == val {
self.cells[a][b].invalid = true;
return;
}
}
for i in 0..SIZE {
if i == b {
continue;
}
if self.cells[i][b].value == val {
self.cells[a][b].invalid = true;
return;
}
}
let (row, col) = (a / 3, b / 3);
for i in 3 * row..3 * row + 3 {
for j in 3 * col..3 * col + 3 {
if i == a && j == b {
continue;
}
if self.cells[i][j].value == val {
self.cells[a][b].invalid = true;
return;
}
}
}
self.cells[a][b].invalid = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_sdm() {
let got = Gameboard::load_sdm("static/puzzle.sdm");
let want = Gameboard::from_cells([
[0, 1, 6, 4, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 9, 0, 0, 0],
[4, 0, 0, 0, 0, 0, 0, 6, 2],
[0, 7, 0, 2, 3, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 3],
[0, 0, 3, 0, 8, 7, 0, 4, 0],
[9, 6, 0, 0, 0, 0, 0, 0, 5],
[0, 0, 0, 8, 0, 0, 0, 0, 7],
[0, 0, 0, 0, 0, 6, 8, 2, 0],
]);
assert_eq!(got, want);
}
}

View File

@@ -0,0 +1,57 @@
//! Gameboard controller
use piston::GenericEvent;
use crate::Gameboard;
pub struct GameboardController {
pub gameboard: Gameboard,
pub selected_cell: Option<[usize; 2]>,
cursor_pos: [f64; 2],
}
impl GameboardController {
pub fn new(gameboard: Gameboard) -> GameboardController {
GameboardController {
gameboard: gameboard,
selected_cell: None,
cursor_pos: [0.0; 2],
}
}
pub fn event<E: GenericEvent>(&mut self, pos: [f64; 2], size: f64, e: &E) {
use piston::input::{Button, Key, MouseButton};
if let Some(pos) = e.mouse_cursor_args() {
self.cursor_pos = pos;
}
if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
let x = self.cursor_pos[0] - pos[0];
let y = self.cursor_pos[1] - pos[1];
if x >= 0.0 && x < size && y >= 0.0 && y < size {
let cell_x = (x / size * 9.0) as usize;
let cell_y = (y / size * 9.0) as usize;
self.selected_cell = Some([cell_x, cell_y]);
}
}
if let Some(Button::Keyboard(key)) = e.press_args() {
if let Some(ind) = self.selected_cell {
match key {
Key::D1 => self.gameboard.set(ind, 1),
Key::D2 => self.gameboard.set(ind, 2),
Key::D3 => self.gameboard.set(ind, 3),
Key::D4 => self.gameboard.set(ind, 4),
Key::D5 => self.gameboard.set(ind, 5),
Key::D6 => self.gameboard.set(ind, 6),
Key::D7 => self.gameboard.set(ind, 7),
Key::D8 => self.gameboard.set(ind, 8),
Key::D9 => self.gameboard.set(ind, 9),
_ => {}
}
}
}
}
}

View File

@@ -0,0 +1,210 @@
//! Gameboard view.
use graphics::character::CharacterCache;
use graphics::types::Color;
use graphics::{Context, Graphics};
use crate::gameboard_controller::GameboardController;
pub struct GameboardViewSettings {
pub position: [f64; 2],
pub size: f64,
pub background_color: Color,
pub border_color: Color,
pub board_edge_color: Color,
pub section_edge_color: Color,
pub cell_edge_color: Color,
pub board_edge_radius: f64,
pub section_edge_radius: f64,
pub cell_edge_radius: f64,
pub selected_cell_background_color: Color,
pub text_color: Color,
pub loaded_cell_background_color: Color,
pub invalid_cell_background_color: Color,
pub invalid_selected_cell_background_color: Color,
pub completed_background_color: Color,
}
impl GameboardViewSettings {
pub fn new() -> GameboardViewSettings {
GameboardViewSettings {
position: [10.0; 2],
size: 400.0,
background_color: [0.8, 0.8, 1.0, 1.0],
border_color: [0.0, 0.0, 0.2, 1.0],
board_edge_color: [0.0, 0.0, 0.2, 1.0],
section_edge_color: [0.0, 0.0, 0.2, 1.0],
cell_edge_color: [0.0, 0.0, 0.2, 1.0],
board_edge_radius: 3.0,
section_edge_radius: 2.0,
cell_edge_radius: 1.0,
selected_cell_background_color: [0.9, 0.9, 1.0, 1.0],
text_color: [0.0, 0.0, 0.1, 1.0],
loaded_cell_background_color: [1.0, 1.0, 1.0, 1.0],
invalid_cell_background_color: [1.0, 0.0, 0.0, 1.0],
invalid_selected_cell_background_color: [1.0, 0.0, 0.5, 1.0],
completed_background_color: [0.0, 1.0, 0.0, 1.0],
}
}
}
pub struct GameboardView {
pub settings: GameboardViewSettings,
}
impl GameboardView {
pub fn new(settings: GameboardViewSettings) -> GameboardView {
GameboardView { settings: settings }
}
pub fn draw<G: Graphics, C>(
&self,
controller: &GameboardController,
glyphs: &mut C,
c: &Context,
g: &mut G,
) where
C: CharacterCache<Texture = G::Texture>,
{
use graphics::{Image, Line, Rectangle, Transformed};
let ref settings = self.settings;
let board_rect = [
settings.position[0],
settings.position[1],
settings.size,
settings.size,
];
if controller.gameboard.completed {
Rectangle::new(settings.completed_background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
} else {
Rectangle::new(settings.background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
for i in 0..9 {
for j in 0..9 {
if controller.gameboard.cells[i][j].loaded {
color_cell(
settings,
[j, i],
settings.loaded_cell_background_color,
c,
g,
);
} else if controller.gameboard.cells[i][j].invalid {
color_cell(
settings,
[j, i],
settings.invalid_cell_background_color,
c,
g,
);
}
}
}
if let Some(ind) = controller.selected_cell {
let cell = controller.gameboard.cells[ind[1]][ind[0]];
let color = if !cell.loaded {
if !cell.invalid {
settings.selected_cell_background_color
} else {
settings.invalid_selected_cell_background_color
}
} else {
settings.loaded_cell_background_color
};
color_cell(settings, ind, color, c, g);
}
}
let text_image = Image::new_color(settings.text_color);
let cell_size = settings.size / 9.0;
for j in 0..9 {
for i in 0..9 {
if let Some(ch) = controller.gameboard.char([i, j]) {
let pos = [
settings.position[0] + i as f64 * cell_size + 15.0,
settings.position[1] + j as f64 * cell_size + 34.0,
];
if let Ok(character) = glyphs.character(34, ch) {
let ch_x = pos[0] + character.left();
let ch_y = pos[1] - character.top();
let text_image = text_image.src_rect([
character.atlas_offset[0],
character.atlas_offset[1],
character.atlas_size[0],
character.atlas_size[1],
]);
text_image.draw(
character.texture,
&c.draw_state,
c.transform.trans(ch_x, ch_y),
g,
);
}
}
}
}
let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius);
let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius);
for i in 0..9 {
let x = settings.position[0] + i as f64 / 9.0 * settings.size;
let y = settings.position[1] + i as f64 / 9.0 * settings.size;
let x2 = settings.position[0] + settings.size;
let y2 = settings.position[1] + settings.size;
let vline = [x, settings.position[1], x, y2];
let hline = [settings.position[0], y, x2, y];
if (i % 3) == 0 {
section_edge.draw(vline, &c.draw_state, c.transform, g);
section_edge.draw(hline, &c.draw_state, c.transform, g);
} else {
cell_edge.draw(vline, &c.draw_state, c.transform, g);
cell_edge.draw(hline, &c.draw_state, c.transform, g);
}
}
Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
}
}
fn color_cell<G: Graphics>(
settings: &GameboardViewSettings,
ind: [usize; 2],
color: [f32; 4],
c: &Context,
g: &mut G,
) {
use graphics::Rectangle;
let cell_size = settings.size / 9.0;
let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size];
let cell_rect = [
settings.position[0] + pos[0],
settings.position[1] + pos[1],
cell_size,
cell_size,
];
Rectangle::new(color).draw(cell_rect, &c.draw_state, c.transform, g);
}

View File

@@ -0,0 +1,57 @@
//////#![deny(missing_docs)]
//! An Sudoko please.
extern crate glutin_window;
use glutin_window::GlutinWindow;
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use piston::event_loop::{EventSettings, Events};
use piston::{EventLoop, RenderEvent, WindowSettings};
pub use crate::gameboard::Gameboard;
pub use crate::gameboard_controller::GameboardController;
pub use crate::gameboard_view::{GameboardView, GameboardViewSettings};
mod gameboard;
mod gameboard_controller;
mod gameboard_view;
fn main() {
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("Sudoku", (640, 480))
.exit_on_esc(true)
.graphics_api(opengl)
.vsync(true);
let mut window: GlutinWindow = settings.build().expect("could not create window");
let mut events = Events::new(EventSettings::new().lazy(true));
let mut gl = GlGraphics::new(opengl);
let args: Vec<_> = std::env::args().collect();
let infile = args.get(1).expect("usage: sudoku <sdm-file>");
let gameboard = Gameboard::load_sdm(infile);
let mut gameboard_controller = GameboardController::new(gameboard);
let gameboard_view_settings = GameboardViewSettings::new();
let gameboard_view = GameboardView::new(gameboard_view_settings);
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings)
.expect("Could not load font");
while let Some(e) = events.next(&mut window) {
gameboard_controller.event(
gameboard_view.settings.position,
gameboard_view.settings.size,
&e,
);
if let Some(args) = e.render_args() {
gl.draw(args.viewport(), |c, g| {
use graphics::clear;
clear([1.0; 4], g);
gameboard_view.draw(&gameboard_controller, glyphs, &c, g);
})
}
}
}

View File

@@ -0,0 +1 @@
517962483236847915498351762371695248654218397829734156765129834142583679983476520

View File

@@ -0,0 +1 @@
016400000200009000400000062070230100100000003003087040960000005000800007000006820

View File

@@ -1,11 +1,6 @@
/custom_types/*
/hello/*
/primitives/*
/variable_bindings/*
/types/*
!/custom_types/*.rs
!/hello/*.rs
!/primitives/*.rs
!/variable_bindings/*.rs
!/types/*.rs
*.d/out
*.d/**/out
expression
hello
primitives
variable_bindings

View File

@@ -0,0 +1,18 @@
use std::convert::From;
#[allow(dead_code)]
#[derive(Debug)]
struct Number {
value: i32,
}
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let num = Number::from(30);
println!("My number is {:?}", num);
}

View File

@@ -0,0 +1,19 @@
use std::convert::Into;
#[allow(dead_code)]
#[derive(Debug)]
struct Number {
value: i32,
}
impl Into<Number> for i32 {
fn into(self) -> Number {
Number { value: self }
}
}
fn main() {
let int = 5;
let num: Number = int.into();
println!("My number is {:?}", num);
}

View File

@@ -0,0 +1,16 @@
use std::fmt;
struct Circle {
radius: i32,
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}

View File

@@ -0,0 +1,7 @@
fn main() {
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();
let sum = parsed + turbo_parsed;
println!("Sum: {:?}", sum);
}

View File

@@ -0,0 +1,28 @@
use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug, PartialEq)]
struct EvenNumber(i32);
impl TryFrom<i32> for EvenNumber {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value % 2 == 0 {
Ok(EvenNumber(value))
} else {
Err(())
}
}
}
fn main() {
assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
assert_eq!(EvenNumber::try_from(5), Err(()));
let result: Result<EvenNumber, ()> = 8i32.try_into();
assert_eq!(result, Ok(EvenNumber(8)));
let result: Result<EvenNumber, ()> = 5i32.try_into();
assert_eq!(result, Err(()));
}

View File

@@ -1,3 +1,4 @@
#[allow(dead_code)]
#[derive(Debug)]
struct Person {
name: String,

View File

@@ -0,0 +1,25 @@
#[allow(path_statements)]
#[allow(unused_must_use)]
fn main() {
let x = 5;
x;
x + 1;
15;
let x = 5u32;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
x_cube + x_squared + x
};
let z = {
2 * x;
};
println!("x is {:?}", x);
println!("y is {:?}", y);
println!("z is {:?}", z);
}

View File

@@ -0,0 +1,13 @@
fn main() {
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}

View File

@@ -0,0 +1,13 @@
fn main() {
for n in 1..=100 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}

View File

@@ -0,0 +1,12 @@
fn main() {
let names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter() {
match name {
&"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
println!("names: {:?}", names);
}

View File

@@ -0,0 +1,12 @@
fn main() {
let names = vec!["Bob", "Frank", "Ferris"];
for name in names.into_iter() {
match name {
"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
//println!("names: {:?}", names);
}

View File

@@ -0,0 +1,12 @@
fn main() {
let mut names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter_mut() {
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
}
println!("names: {:?}", names);
}

View File

@@ -0,0 +1,23 @@
fn main() {
let n = 5;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
let big_n = if n < 10 && n > -10 {
println!(", and is a small number, increase net-fold");
10 * n
} else {
println!(", and is a big number, halve the number");
n / 2
};
println!("{} -> {}", n, big_n);
}

View File

@@ -0,0 +1,25 @@
fn main() {
let number = Some(7);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;
if let Some(i) = number {
println!("Matched {:?}!", i);
}
if let Some(i) = letter {
println!("Matched {:?}!", i);
} else {
println!("Didn't match a number. Let's go with a letter!");
}
let i_like_letters = false;
if let Some(i) = emoticon {
println!("Matched {:?}!", i);
} else if i_like_letters {
println!("Didn't match a number. Let's go with a letter!");
} else {
println!("I don't like letters. Let's go with an emoticon :)!");
}
}

View File

@@ -0,0 +1,27 @@
enum Foo {
Bar,
Baz,
Qux(u32),
}
fn main() {
let a = Foo::Bar;
let b = Foo::Baz;
let c = Foo::Qux(100);
if let Foo::Bar = a {
println!("a is a foobar");
}
if let Foo::Bar = b {
println!("b is foobar");
}
if let Foo::Qux(value) = c {
println!("c is {}", value);
}
if let Foo::Qux(value @ 100) = c {
println!("c is one hundred (value: {:?})", value);
}
}

View File

@@ -0,0 +1,11 @@
enum Foo {
Bar,
}
fn main() {
let a = Foo::Bar;
if let Foo::Bar = a {
println!("a is foobar");
}
}

View File

@@ -0,0 +1,16 @@
use std::str::FromStr;
fn get_count_item(s: &str) -> (u64, &str) {
let mut it = s.split(' ');
let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
panic!("Can't segment count item pair: '{s}'");
};
let Ok(count) = u64::from_str(count_str) else {
panic!("Can't parse integer: '{count_str}'");
};
(count, item)
}
fn main() {
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));
}

View File

@@ -0,0 +1,18 @@
#![allow(unreachable_code)]
#![allow(unused_labels)]
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
break 'outer;
}
println!("This point will never be reached");
}
println!("Exited the outer loop");
}

View File

@@ -0,0 +1,13 @@
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
assert_eq!(result, 20);
}

View File

@@ -0,0 +1,23 @@
fn main() {
let mut count = 0u32;
println!("Let's count until infinity!");
loop {
count += 1;
if count == 3 {
println!("three");
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
break;
}
}
}

View File

@@ -0,0 +1,14 @@
fn age() -> u32 {
15
}
fn main() {
println!("Tell me what type of person you are");
match age() {
0 => println!("I haven't celebrated my first birthday yet"),
n @ 1..=12 => println!("I'm a child of age {:?}", n),
n @ 13..=19 => println!("I'm a teen of age {:?}", n),
n => println!("I'm an old person of age {:?}", n),
}
}

View File

@@ -0,0 +1,11 @@
fn some_number() -> Option<u32> {
Some(42)
}
fn main() {
match some_number() {
Some(n @ 42) => println!("The Answer: {}!", n),
Some(n) => println!("Not interesting... {}", n),
_ => (),
}
}

View File

@@ -0,0 +1,30 @@
#[allow(dead_code)]
enum Color {
Red,
Blue,
Green,
RGB(u32, u32, u32),
HSV(u32, u32, u32),
HSL(u32, u32, u32),
CMY(u32, u32, u32),
CMYK(u32, u32, u32, u32),
}
fn main() {
let color = Color::RGB(122, 17, 40);
println!("What color is it?");
match color {
Color::Red => println!("The color is Red!"),
Color::Blue => println!("The color is Blue!"),
Color::Green => println!("The color is Green!"),
Color::RGB(r, g, b) => println!("Red: {}, green: {}, and blue: {}!", r, g, b),
Color::HSV(h, s, v) => println!("Hue: {}, saturation: {}, value: {}!", h, s, v),
Color::HSL(h, s, l) => println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l),
Color::CMY(c, m, y) => println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y),
Color::CMYK(c, m, y, k) => println!(
"Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
c, m, y, k
),
}
}

View File

@@ -0,0 +1,29 @@
fn main() {
let reference = &4;
match reference {
&val => println!("Got a value via destructuring: {:?}", val),
}
match *reference {
val => println!("Got a value via dereferencing: {:?}", val),
}
let _not_a_reference = 3;
let ref _is_a_reference = 3;
let value = 5;
let mut mut_value = 6;
match value {
ref r => println!("Got a reference to a value: {:?}", r),
}
match mut_value {
ref mut m => {
*m += 10;
println!("We added 10. `mut_value`: {:?}", m);
}
}
}

View File

@@ -0,0 +1,27 @@
fn main() {
let array = [1, -2, 6];
match array {
[0, second, third] => println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third),
[1, _, third] => println!(
"array[0] = 1, array[2] = {} and array[1] was ignored",
third
),
[-1, second, ..] => println!(
"array[0] = -1, array[1] = {} and all the other ones were ignored",
second
),
[3, second, tail @ ..] => println!(
"array[0] = 3, array[1] = {} and the other elements were {:?}",
second, tail
),
[first, middle @ .., last] => println!(
"array[0] = {}, middle = {:?}, array[2] = {}",
first, middle, last
),
}
}

View File

@@ -0,0 +1,19 @@
fn main() {
struct Foo {
x: (u32, u32),
y: u32,
}
let foo = Foo { x: (1, 2), y: 3 };
match foo {
Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {}", b, y),
Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),
Foo { y, .. } => println!("y = {}, we don't care about x", y),
}
let faa = Foo { x: (1, 2), y: 3 };
let Foo { x: x0, y: y0 } = faa;
println!("Outside: x0 = {x0:?}, y0 = {y0}");
}

View File

@@ -0,0 +1,12 @@
fn main() {
let triple = (0, -2, 3);
println!("Tell me about {:?}", triple);
match triple {
(0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
(1, ..) => println!("First is `1` and the rest doesn't matter"),
(.., 2) => println!("last is `2` and the rest doesn't matter"),
(3, .., 4) => println!("First is `3`, last is `4`, and the rest doesn't matter"),
_ => println!("It doesn't matter what they are"),
}
}

View File

@@ -0,0 +1,16 @@
#[allow(dead_code)]
enum Temperature {
Celsius(i32),
Fahrenheit(i32),
}
fn main() {
let temperature = Temperature::Celsius(35);
match temperature {
Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t),
Temperature::Celsius(t) => println!("{}C is below 30 Celsius", t),
Temperature::Fahrenheit(t) if t > 86 => println!("{}F is above 86 Fahrenheit", t),
Temperature::Fahrenheit(t) => println!("{}F is below 86 Fahrenheit", t),
}
}

View File

@@ -0,0 +1,9 @@
fn main() {
let number: u8 = 4;
match number {
i if i == 0 => println!("Zero"),
i if i > 0 => println!("Greater than zero"),
_ => unreachable!("Should never happen."),
}
}

View File

@@ -0,0 +1,19 @@
fn main() {
let number = 13;
println!("Tell me about {}", number);
match number {
1 => println!("One!"),
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
13..=19 => println!("A teen"),
_ => println!("Ain't special"),
}
let boolean = true;
let binary = match boolean {
false => 0,
true => 1,
};
println!("{} -> {}", boolean, binary);
}

View File

@@ -0,0 +1,17 @@
fn main() {
let mut n = 1;
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
n += 1;
}
}

View File

@@ -0,0 +1,13 @@
fn main() {
let mut optional = Some(0);
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
}
}

View File

@@ -1,3 +1,4 @@
#[allow(dead_code)]
#[derive(Debug)]
struct Person<'a> {
name: &'a str,

View File

@@ -20,6 +20,6 @@ impl fmt::Display for List {
}
fn main() {
let v = List(vec![1, 2, 3]);
let v = List(vec![6, 0, 6, 8, 0, 8, 9, 1, 1, 1, 3, 1, 2]);
println!("{}", v);
}

View File

@@ -22,5 +22,14 @@ fn main() {
println!("borrow a section of the array as a slice");
analyze_slice(&ys[1..4]);
// println!("{}", xs[5]);
let empty_array: [u32; 0] = [];
assert_eq!(&empty_array, &[]);
assert_eq!(&empty_array, &[][..]);
for i in 0..xs.len() + 1 {
match xs.get(i) {
Some(xval) => println!("{}: {}", i, xval),
None => println!("Slow down! {} is too far!", i),
}
}
}

View File

@@ -3,6 +3,8 @@ fn main() {
println!("1 - 2 = {}", 1i32 - 2);
println!("1e4 is {}, -2.5e-3 is {}", 1e4, -2.5e-3);
println!("true AND false is {}", true && false);
println!("true OR false is {}", true || false);
println!("NOT true is {}", !true);

View File

@@ -1,3 +1,5 @@
#[allow(unused_variables)]
#[allow(unused_assignments)]
fn main() {
let logical: bool = true;

View File

@@ -0,0 +1,15 @@
type NanoSecond = u64;
type Inch = u64;
type U64 = u64;
fn main() {
let nanoseconds: NanoSecond = 5 as U64;
let inches: Inch = 2 as U64;
println!(
"{} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches
);
}

View File

@@ -1,15 +0,0 @@
type NanoSecond = u64;
type Inch = u64;
#[allow(non_camel_case_types)]
type u64_t = u64;
fn main() {
let nanoseconds: NanoSecond = 5 as u64_t;
let inches: Inch = 2 as u64_t;
println!("{} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches);
}

View File

@@ -13,4 +13,3 @@ fn main() {
let shadowed_binding = 2;
println!("shadowed in outer block: {}", shadowed_binding);
}

View File

@@ -11,5 +11,5 @@ fn main() {
let _unused_variable = 3u32;
let noisy_unused_variable = 2u32;
let _noisy_unused_variable = 2u32;
}