Skip to content

Instantly share code, notes, and snippets.

@kujirahand
Last active January 25, 2022 01:29
Show Gist options
  • Save kujirahand/bb3eb3208e9154266f2c7be4dddbaf31 to your computer and use it in GitHub Desktop.
Save kujirahand/bb3eb3208e9154266f2c7be4dddbaf31 to your computer and use it in GitHub Desktop.
複数行の計算、および、変数代入の機能がある最小計算言語
%{
// プログラムのヘッダを指定
package main
import (
"os"
"fmt"
"strconv"
"bufio"
)
%}
%union {
num float64 // 数値計算のため
word string // 変数名のため
token int // その他の記号のため
}
// プログラムの構成要素を指定
%type<num> program line expr let
%token<num> NUMBER LF
%token<word> WORD
%token<token> '(',')','='
// 演算の優先度の指定
%left '+','-'
%left '*','/','%'
%%
// 文法規則を指定
// program は line の繰り返し
program : line | program line
// line は 代入文か計算式
line
: let LF { $$ = $1 }
| expr LF { $$ = $1; fmt.Println("計算結果:", $1) }
| LF
// 代入文
let
: WORD '=' expr { hensu[$1] = $3 }
// 計算式
expr
: NUMBER
| WORD { $$ = hensu[$1] }
| expr '+' expr { $$ = $1 + $3 }
| expr '-' expr { $$ = $1 - $3 }
| expr '*' expr { $$ = $1 * $3 }
| expr '/' expr { $$ = $1 / $3 }
| expr '%' expr { $$ = float64(int($1) % int($3)) }
| '(' expr ')' { $$ = $2 }
%%
// 変数の記憶領域を作成
var hensu = map[string]float64{}
// 最低限必要な構造体を定義
type Lexer struct {
src []rune
index int
result float64
}
// トークンを一つずつ返す
func (p *Lexer) Lex(lval *yySymType) int {
for p.index < len(p.src) {
c := p.src[p.index]
// スペースなら飛ばす --- (*1)
if c == ' ' || c == '\t' {
p.index++
continue
}
// 数値の場合 --- (*2)
if isDigit(c) {
s := ""
for p.index < len(p.src) {
c = p.src[p.index]
if isDigit(c) || c == '.' {
s += string(c)
p.index++
continue
}
break
}
lval.num, _ = strconv.ParseFloat(s, 64)
return NUMBER
}
p.index++ // これ以降1文字1トークン
// プログラムの区切り記号の場合 -- (*3)
if c == ';' || c == '\n' {
return LF
}
// 演算子の場合 --- (*4)
if isOperator(c) {
return int(c)
}
// 変数の場合 --- (*5)
if 'a' <= c && c <= 'z' {
lval.word = string(c)
return WORD
}
}
return -1
}
func isOperator(c rune) bool { // 演算子か
return c == '+' || c == '-' ||
c == '*' || c == '/' || c == '%' ||
c == '(' || c == ')' || c == '='
}
func isDigit(c rune) bool { // 数字か
return '0' <= c && c <= '9'
}
// エラー報告用
func (p *Lexer) Error(e string) {
fmt.Println("[error] " + e)
}
// コードを実行
func run(code string) {
lexer := &Lexer{
src: []rune(code + "\n"),
index:0,
}
yyDebug = 1
yyErrorVerbose = true
yyParse(lexer)
}
// メイン関数
func main() {
if len(os.Args) >= 2 {
run(os.Args[1]); return
}
sc := bufio.NewScanner(os.Stdin)
for {
print("> ")
sc.Scan()
run(sc.Text())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment