Skip to content

Instantly share code, notes, and snippets.

@macrat
Last active January 10, 2018 10:35
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 macrat/78617f0dec7ae1974a32ddfc8dc40d76 to your computer and use it in GitHub Desktop.
Save macrat/78617f0dec7ae1974a32ddfc8dc40d76 to your computer and use it in GitHub Desktop.
simplexerとgoyaccの組み合わせサンプル。
%{
package main
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/macrat/simplexer"
)
type Expression interface {
Calc() float64
}
type Number float64
func (n Number) Calc() float64 {
return float64(n)
}
type Operator struct {
Left Expression
Operator string
Right Expression
}
func (s Operator) Calc() float64 {
switch s.Operator {
case "+":
return s.Left.Calc() + s.Right.Calc()
case "-":
return s.Left.Calc() - s.Right.Calc()
case "*":
return s.Left.Calc() * s.Right.Calc()
case "/":
return s.Left.Calc() / s.Right.Calc()
}
return 0
}
%}
%union{
token *simplexer.Token
expr Expression
}
%type<expr> program expression operator
%token<token> NUMBER L1_OPERATOR L2_OPERATOR
%left L1_OPERATOR
%left L2_OPERATOR
%%
program
: expression
{
$$ = $1
yylex.(*Lexer).result = $$
}
expression
: NUMBER
{
f, _ := strconv.ParseFloat($1.Literal, 64)
$$ = Number(f)
}
| operator
{
$$ = $1
}
operator
: expression L2_OPERATOR expression
{
$$ = Operator{
Left: $1,
Operator: $2.Literal,
Right: $3,
}
}
| expression L1_OPERATOR expression
{
$$ = Operator{
Left: $1,
Operator: $2.Literal,
Right: $3,
}
}
%%
type Lexer struct {
lexer *simplexer.Lexer
lastToken *simplexer.Token
result Expression
}
func NewLexer(reader io.Reader) *Lexer {
l := simplexer.NewLexer(reader)
l.TokenTypes = []simplexer.TokenType{
simplexer.NewRegexpTokenType(NUMBER, `-?[0-9]+(\.[0-9]+)?`),
simplexer.NewRegexpTokenType(L1_OPERATOR, `[-+]`),
simplexer.NewRegexpTokenType(L2_OPERATOR, `[*/]`),
}
return &Lexer{ lexer: l }
}
func (l *Lexer) Lex(lval *yySymType) int {
token, err := l.lexer.Scan()
if err != nil {
if e, ok := err.(simplexer.UnknownTokenError); ok {
fmt.Fprintln(os.Stderr, e.Error() + ":")
fmt.Fprintln(os.Stderr, l.lexer.GetLastLine())
fmt.Fprintln(os.Stderr, strings.Repeat(" ", e.Position.Column) + strings.Repeat("^", len(e.Literal)))
} else {
l.Error(err.Error())
}
os.Exit(1)
}
if token == nil {
return -1
}
lval.token = token
l.lastToken = token
return int(token.Type.GetID())
}
func (l *Lexer) Error(e string) {
fmt.Fprintln(os.Stderr, e + ":")
fmt.Fprintln(os.Stderr, l.lexer.GetLastLine())
fmt.Fprintln(os.Stderr, strings.Repeat(" ", l.lastToken.Position.Column) + strings.Repeat("^", len(l.lastToken.Literal)))
}
func main() {
lexer := NewLexer(strings.NewReader("1 + 2 * 3 abc - 4 / 5"))
yyParse(lexer)
if lexer.result != nil {
fmt.Println(lexer.result)
fmt.Println("=", lexer.result.Calc())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment