Compare commits
16 Commits
arghing
...
6e50ec65f8
Author | SHA1 | Date | |
---|---|---|---|
6e50ec65f8
|
|||
ca812add16
|
|||
460741740b
|
|||
7204f08177
|
|||
505e04613f
|
|||
7f4f627769
|
|||
dc8045a20e
|
|||
41f53c34c0
|
|||
6c0083e094
|
|||
7f2f579241
|
|||
7d2ac20ce1
|
|||
8f38a3b271
|
|||
96f5f85a27
|
|||
6cf80e09b1
|
|||
30ed115ed0
|
|||
8376608a1e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@
|
||||
**/target/
|
||||
/hello_world/main
|
||||
/aoc*/**/input
|
||||
/arduino/build-*/
|
||||
|
2
arduino/.envrc
Normal file
2
arduino/.envrc
Normal file
@@ -0,0 +1,2 @@
|
||||
export ARDMK_DIR=/usr/share/arduino
|
||||
export ARDMK_VENDOR=archlinux-arduino
|
3
arduino/Makefile
Normal file
3
arduino/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
BOARD_TAG = uno
|
||||
|
||||
include $(ARDMK_DIR)/Arduino.mk
|
31
arduino/sos.ino
Normal file
31
arduino/sos.ino
Normal 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);
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
# argh command line parser
|
||||
|
||||
> NOTE: much of this is lifted from
|
||||
> https://blog.gopheracademy.com/advent-2014/parsers-lexers/
|
34
argh/argh.go
34
argh/argh.go
@@ -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...)
|
||||
}
|
@@ -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)
|
||||
}
|
12
argh/go.mod
12
argh/go.mod
@@ -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
|
||||
)
|
12
argh/go.sum
12
argh/go.sum
@@ -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=
|
46
argh/node.go
46
argh/node.go
@@ -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{}
|
@@ -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]]
|
||||
}
|
346
argh/parser.go
346
argh/parser.go
@@ -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}
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
1028
argh/parser_test.go
1028
argh/parser_test.go
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
159
argh/scanner.go
159
argh/scanner.go
@@ -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
|
||||
}
|
@@ -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 == '_'
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
1876
piston-tutorials/getting-started/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
piston-tutorials/getting-started/Cargo.toml
Normal file
17
piston-tutorials/getting-started/Cargo.toml
Normal 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"
|
70
piston-tutorials/getting-started/src/main.rs
Normal file
70
piston-tutorials/getting-started/src/main.rs
Normal 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
1876
piston-tutorials/sudoku/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
piston-tutorials/sudoku/Cargo.toml
Normal file
12
piston-tutorials/sudoku/Cargo.toml
Normal 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"
|
BIN
piston-tutorials/sudoku/assets/FiraSans-Regular.ttf
Normal file
BIN
piston-tutorials/sudoku/assets/FiraSans-Regular.ttf
Normal file
Binary file not shown.
99
piston-tutorials/sudoku/assets/LICENSE
Normal file
99
piston-tutorials/sudoku/assets/LICENSE
Normal 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.
|
155
piston-tutorials/sudoku/src/gameboard.rs
Normal file
155
piston-tutorials/sudoku/src/gameboard.rs
Normal 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);
|
||||
}
|
||||
}
|
57
piston-tutorials/sudoku/src/gameboard_controller.rs
Normal file
57
piston-tutorials/sudoku/src/gameboard_controller.rs
Normal 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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
210
piston-tutorials/sudoku/src/gameboard_view.rs
Normal file
210
piston-tutorials/sudoku/src/gameboard_view.rs
Normal 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);
|
||||
}
|
57
piston-tutorials/sudoku/src/main.rs
Normal file
57
piston-tutorials/sudoku/src/main.rs
Normal 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);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
1
piston-tutorials/sudoku/static/puzzle-almost-solved.sdm
Normal file
1
piston-tutorials/sudoku/static/puzzle-almost-solved.sdm
Normal file
@@ -0,0 +1 @@
|
||||
517962483236847915498351762371695248654218397829734156765129834142583679983476520
|
1
piston-tutorials/sudoku/static/puzzle.sdm
Normal file
1
piston-tutorials/sudoku/static/puzzle.sdm
Normal file
@@ -0,0 +1 @@
|
||||
016400000200009000400000062070230100100000003003087040960000005000800007000006820
|
17
rustbyexample/.gitignore
vendored
17
rustbyexample/.gitignore
vendored
@@ -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
|
||||
|
18
rustbyexample/conversion.d/from_into.rs
Normal file
18
rustbyexample/conversion.d/from_into.rs
Normal 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);
|
||||
}
|
19
rustbyexample/conversion.d/from_into2.rs
Normal file
19
rustbyexample/conversion.d/from_into2.rs
Normal 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);
|
||||
}
|
16
rustbyexample/conversion.d/string.rs
Normal file
16
rustbyexample/conversion.d/string.rs
Normal 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());
|
||||
}
|
7
rustbyexample/conversion.d/string2.rs
Normal file
7
rustbyexample/conversion.d/string2.rs
Normal 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);
|
||||
}
|
28
rustbyexample/conversion.d/try_from_try_into.rs
Normal file
28
rustbyexample/conversion.d/try_from_try_into.rs
Normal 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(()));
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
struct Person {
|
||||
name: String,
|
25
rustbyexample/expression.rs
Normal file
25
rustbyexample/expression.rs
Normal 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);
|
||||
}
|
13
rustbyexample/flow_control.d/for.rs
Normal file
13
rustbyexample/flow_control.d/for.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
13
rustbyexample/flow_control.d/for2.rs
Normal file
13
rustbyexample/flow_control.d/for2.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
12
rustbyexample/flow_control.d/for3.rs
Normal file
12
rustbyexample/flow_control.d/for3.rs
Normal 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);
|
||||
}
|
12
rustbyexample/flow_control.d/for4.rs
Normal file
12
rustbyexample/flow_control.d/for4.rs
Normal 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);
|
||||
}
|
12
rustbyexample/flow_control.d/for5.rs
Normal file
12
rustbyexample/flow_control.d/for5.rs
Normal 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);
|
||||
}
|
23
rustbyexample/flow_control.d/if_else.rs
Normal file
23
rustbyexample/flow_control.d/if_else.rs
Normal 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);
|
||||
}
|
18
rustbyexample/flow_control.d/loop.d/nested.rs
Normal file
18
rustbyexample/flow_control.d/loop.d/nested.rs
Normal 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");
|
||||
}
|
13
rustbyexample/flow_control.d/loop.d/return.rs
Normal file
13
rustbyexample/flow_control.d/loop.d/return.rs
Normal 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);
|
||||
}
|
23
rustbyexample/flow_control.d/loop.rs
Normal file
23
rustbyexample/flow_control.d/loop.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
14
rustbyexample/flow_control.d/match.d/binding.rs
Normal file
14
rustbyexample/flow_control.d/match.d/binding.rs
Normal 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),
|
||||
}
|
||||
}
|
11
rustbyexample/flow_control.d/match.d/binding2.rs
Normal file
11
rustbyexample/flow_control.d/match.d/binding2.rs
Normal 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),
|
||||
_ => (),
|
||||
}
|
||||
}
|
@@ -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
|
||||
),
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
),
|
||||
}
|
||||
}
|
@@ -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}");
|
||||
}
|
@@ -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"),
|
||||
}
|
||||
}
|
16
rustbyexample/flow_control.d/match.d/guard.rs
Normal file
16
rustbyexample/flow_control.d/match.d/guard.rs
Normal 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),
|
||||
}
|
||||
}
|
9
rustbyexample/flow_control.d/match.d/guard2.rs
Normal file
9
rustbyexample/flow_control.d/match.d/guard2.rs
Normal 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."),
|
||||
}
|
||||
}
|
19
rustbyexample/flow_control.d/match.rs
Normal file
19
rustbyexample/flow_control.d/match.rs
Normal 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);
|
||||
}
|
17
rustbyexample/flow_control.d/while.rs
Normal file
17
rustbyexample/flow_control.d/while.rs
Normal 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;
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
struct Person<'a> {
|
||||
name: &'a str,
|
@@ -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);
|
||||
}
|
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
@@ -1,3 +1,5 @@
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_assignments)]
|
||||
fn main() {
|
||||
let logical: bool = true;
|
||||
|
15
rustbyexample/types.d/alias.rs
Normal file
15
rustbyexample/types.d/alias.rs
Normal 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
|
||||
);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -13,4 +13,3 @@ fn main() {
|
||||
let shadowed_binding = 2;
|
||||
println!("shadowed in outer block: {}", shadowed_binding);
|
||||
}
|
||||
|
@@ -11,5 +11,5 @@ fn main() {
|
||||
|
||||
let _unused_variable = 3u32;
|
||||
|
||||
let noisy_unused_variable = 2u32;
|
||||
let _noisy_unused_variable = 2u32;
|
||||
}
|
Reference in New Issue
Block a user