Skip to content

Instantly share code, notes, and snippets.

@dreampuf
Last active August 22, 2019 19:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dreampuf/5ae11ab5a4701290fc35539460d8824d to your computer and use it in GitHub Desktop.
Save dreampuf/5ae11ab5a4701290fc35539460d8824d to your computer and use it in GitHub Desktop.
hostlist generate by goyacc
%{
package main
import(
"fmt"
"bufio"
"os"
"strconv"
"unicode"
"unicode/utf8"
)
const(
Debug = 4
ErrorVerbose = true
)
type LexerStatus int
const (
INITIAL_STATUS LexerStatus = iota
IN_INT_PART_STATUS
DOT_STATUS
IN_FRAC_PART_STATUS
)
type GoCalcLex struct {
Pos int
Status LexerStatus
Input []byte
}
%}
%union {
int_value int
float_value float64
}
%token <float_value> DOUBLE_LITERAL
%token ADD SUB MUL DIV CR LP RP
%type <float_value> expression term primary_expression
%%
line_list
: line
| line_list line
;
line
: expression CR
{
fmt.Printf(">>%1f\n", $1);
}
;
expression
: term
| expression ADD term
{
$$ = $1 + $3;
}
| expression SUB term
{
$$ = $1 - $3;
}
;
term
: primary_expression
| term MUL primary_expression
{
$$ = $1 * $3;
}
| term DIV primary_expression
{
$$ = $1 / $3;
}
;
primary_expression
: DOUBLE_LITERAL
| LP expression RP
{
$$ = $2;
}
| SUB primary_expression
{
$$ = -$2;
}
;
%%
func (l *GoCalcLex) Lex(lval *yySymType) int {
var Str []rune
l.Status = INITIAL_STATUS
for l.Pos < len(l.Input) {
r, n := utf8.DecodeRune(l.Input[l.Pos:])
if (l.Status == IN_INT_PART_STATUS || l.Status == IN_FRAC_PART_STATUS) &&
!unicode.IsDigit(r) && r != '.' {
lval.float_value, _ = strconv.ParseFloat(string(Str), 64)
return DOUBLE_LITERAL
}
if unicode.IsSpace(r) {
l.Pos = l.Pos + n
if r == '\n' || r == '\r' {
return CR
}
continue
}
Str = append(Str, r)
l.Pos = l.Pos + n
switch {
case r == '+':
return ADD
case r == '-':
return SUB
case r == '*':
return MUL
case r == '/':
return DIV
case r == '(':
return LP
case r == ')':
return RP
case r == '.':
if l.Status == IN_INT_PART_STATUS {
l.Status = DOT_STATUS
} else {
fmt.Println("syntax error in dot")
os.Exit(1)
}
case unicode.IsDigit(r):
if l.Status == INITIAL_STATUS {
l.Status = IN_INT_PART_STATUS
} else if l.Status == DOT_STATUS {
l.Status = IN_FRAC_PART_STATUS
}
default:
fmt.Println("unknow input type %s", string(r))
os.Exit(1)
}
}
return 0
}
func (l *GoCalcLex) Error(s string) {
fmt.Printf("syntax error: %s\n", s)
}
func main(){
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan(){
text := scanner.Text()
text = fmt.Sprintf("%s\n", text)
yyParse(&GoCalcLex{Input: []byte(text)})
}
}
%{
package main
import (
"text/scanner"
"strconv"
"strings"
"fmt"
"reflect"
)
type Expression interface{}
type ParenExpr struct {
SubExpr Expression
}
type Token struct {
token int
literal string
}
type NumExpr struct {
literal string
}
type ChainExpr struct {
current Expression
next Expression
}
type IdentifyExpr struct {
token Token
}
type PairExpr struct {
left Expression
right Expression
}
type RangeExpr struct {
start NumExpr
end NumExpr
}
type NotExpr struct {
supset Expression
exclusive Expression
}
type MultiExpr struct {
current Expression
next Expression
}
%}
%union{
token Token
expr Expression
}
// hostname[111-123!120,324]postfix,hostname2[1-10,134]postfix2,hostname[1-10!(2,3,4)]postfix
// hostname '[' 111 '-' 123 '!' 123 ',' 324 ']' postfix
%type<expr> program
%type<expr> expr range
%token<token> IDENTIFY NUMBER OPENNING_BRACKET CLOSING_BRACKET COMMA DASH OPENNING_PARENTHESIS CLOSING_PARENTHESIS EXCLAMATION
%left IDENTIFY
%left NUMBER
%left OPENNING_BRACKET CLOSING_BRACKET
%left EXCLAMATION
%left MINUS
%left COMMA
%left OPENNING_PARENTHESIS CLOSING_PARENTHESIS
%%
program
: expr
{
$$ = ParenExpr{SubExpr: $1}
yylex.(*Lexer).result = $$
}
| program COMMA expr
{
$$ = MultiExpr{current: $1, next: $3}
yylex.(*Lexer).result = $$
}
;
expr
: IDENTIFY
{
$$ = IdentifyExpr{token: $1}
}
| OPENNING_BRACKET range CLOSING_BRACKET
{
$$ = ParenExpr{SubExpr: $2}
}
| expr IDENTIFY
{
$$ = ChainExpr{current: $1, next: IdentifyExpr{token: $2}}
}
| expr OPENNING_BRACKET range CLOSING_BRACKET
{
$$ = ChainExpr{current: $1, next: ParenExpr{SubExpr: $3}}
}
;
range
: range COMMA range
{
$$ = PairExpr{left: $1, right: $3}
}
| NUMBER
{
$$ = NumExpr{literal: $1.literal}
}
| range EXCLAMATION range
{
$$ = NotExpr{supset: $1, exclusive: $3}
}
| NUMBER DASH NUMBER
{
$$ = RangeExpr{start: NumExpr{literal: $1.literal}, end: NumExpr{literal: $3.literal}}
}
| OPENNING_PARENTHESIS range CLOSING_PARENTHESIS
{
$$ = ParenExpr{SubExpr: $2}
}
;
%%
type Lexer struct {
scanner.Scanner
result Expression
}
func (l *Lexer) Lex(lval *yySymType) int {
token := l.Scan()
lit := l.TokenText()
//fmt.Printf("token: '%d' '%s'\n", token, lit)
tok := int(token)
switch tok {
case scanner.Int:
tok = NUMBER
default:
switch lit {
case "[":
tok = OPENNING_BRACKET
case "]":
tok = CLOSING_BRACKET
case ",":
tok = COMMA
case "-":
tok = DASH
case "!":
tok = EXCLAMATION
case "(":
tok = OPENNING_PARENTHESIS
case ")":
tok = CLOSING_PARENTHESIS
case "":
tok = 0
default:
tok = IDENTIFY
}
}
lval.token = Token{token: tok, literal: lit}
return tok
}
func (l *Lexer) Error(e string) {
fmt.Println(e)
}
func combineLists(lists ...[]string) []string {
tlist := []string{""}
for _, ls := range lists {
nlist := []string{}
if len(ls) == 0 {
nlist = tlist
}
for _, pre := range tlist {
for _, i := range ls {
nlist = append(nlist, pre + i)
}
}
tlist = nlist
}
return tlist
}
func subSet(a, b []string) []string {
t := []string{}
outter:
for _, i := range a {
for _, ii := range b {
if i == ii {
continue outter
}
}
t = append(t, i)
}
return t
}
func generateRange(start, end int, padding int) []string {
t := []string{}
tmp := fmt.Sprintf("%%0%dd", padding)
for i := start; i <= end; i ++ {
t = append(t, fmt.Sprintf(tmp, i))
}
return t
}
func EvalN(e Expression) int {
switch t := e.(type) {
case NumExpr:
num, _ := strconv.Atoi(t.literal)
return num
}
return 0
}
func Eval(e Expression) []string {
if e == nil {
return []string{}
}
//fmt.Printf("eval %s: %v\n", reflect.TypeOf(e), e)
switch t := e.(type) {
case ParenExpr:
return Eval(t.SubExpr)
case ChainExpr:
cur := Eval(t.current)
next := Eval(t.next)
return combineLists(cur, next)
case IdentifyExpr:
return []string{ t.token.literal }
case NotExpr:
supset := Eval(t.supset)
exclusive := Eval(t.exclusive)
return subSet(supset, exclusive)
case RangeExpr:
start := EvalN(t.start)
end := EvalN(t.end)
return generateRange(start, end, len(t.start.literal))
case PairExpr:
left := Eval(t.left)
right := Eval(t.right)
return append(left, right...)
case NumExpr:
return []string{ t.literal }
case MultiExpr:
//fmt.Printf("cur: %v, next: %v\n", Eval(t.current), Eval(t.next))
return append(Eval(t.current), Eval(t.next)...)
default:
fmt.Printf("unsuported %s[%+v]\n", reflect.TypeOf(t), t)
}
return []string{}
}
func Parse(exp string) []string {
//yyDebug = 4
//yyErrorVerbose = true
l := new(Lexer)
l.Init(strings.NewReader(exp))
yyParse(l)
//fmt.Printf("%s\n%+v\n\n", exp, l.result)
return Eval(l.result)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment