Last active
January 25, 2022 01:29
-
-
Save kujirahand/bb3eb3208e9154266f2c7be4dddbaf31 to your computer and use it in GitHub Desktop.
複数行の計算、および、変数代入の機能がある最小計算言語
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%{ | |
// プログラムのヘッダを指定 | |
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