Skip to content

Commit

Permalink
進捗
Browse files Browse the repository at this point in the history
  • Loading branch information
Seasawher committed Oct 18, 2024
1 parent 11f5929 commit eb6d379
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 10 deletions.
1 change: 1 addition & 0 deletions Monkey/Ast/Ast.lean
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ inductive Statement where

deriving Repr, DecidableEq

/-- Statement を文字列に変換する -/
def Statement.toString (stmt: Statement) : String :=
match stmt with
| .letStmt name value => s!"let {name} = {value}"
Expand Down
29 changes: 22 additions & 7 deletions Monkey/Parser/Parser.lean
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ structure Parser where
/-- 次のトークン -/
peekToken : Token

/-- 構文解析エラー -/
errors : List String

/-- Parser を文字列に変換する -/
def Parser.toString (p : Parser) : String :=
s!"⟨curToken={p.curToken}, peekToken={p.peekToken}⟩ : Parser"
Expand All @@ -24,15 +27,20 @@ instance : ToString Parser where
def Parser.nextToken : StateM Parser PUnit := do
let p ← get
let ⟨newToken, newLexer⟩ := p.l.nextToken
let newParser := Parser.mk newLexer p.peekToken newToken
let newParser : Parser := {
l := newLexer,
curToken := p.peekToken,
peekToken := newToken,
errors := p.errors
}
set newParser

/-- 新しくパーサを作る -/
def Parser.new (l : Lexer) : Parser :=
-- Id モナドは無言で取り出せる
let (curToken, l') := l.nextToken
let (peekToken, l'') := l'.nextToken
{ l := l'', curToken, peekToken }
{ l := l'', curToken, peekToken, errors := []}

/-- p の curToken が指定されたトークンと種類が一致するか -/
def Parser.curTokenIs (p : Parser) (t : Token) : Bool :=
Expand All @@ -42,14 +50,22 @@ def Parser.curTokenIs (p : Parser) (t : Token) : Bool :=
def Parser.peekTokenIs (p : Parser) (t : Token) : Bool :=
Token.sameType p.peekToken t

/-- `expectPeek` の過程でエラーが起きたときのために、
エラーメッセージを蓄積する処理 -/
def Parser.peekError (expectedToken : String) : StateM Parser Unit := do
let p ← get
set { p with errors := p.errors ++ [s!"expected next token to be {expectedToken}, got {p.peekToken} instead"] }

open Lean Parser Term in
/-- p の peekToken が指定されたトークン `pat` と種類が一致するか判定。一致した場合は次に進める -/
macro "expectPeek " pat:term rest:doSeqItem* : doElem => do
let pat' : Lean.StrLit := pat.raw.getSubstring?.get! |> toString |> Lean.Syntax.mkStrLit
`(doElem| do
let $pat := (← get).peekToken
| return none
nextToken
$rest*
let $pat := (← get).peekToken
| Parser.peekError $pat'
return none
nextToken
$rest*
)

/-- let 文をパースする -/
Expand All @@ -60,7 +76,6 @@ def Parser.parseLetStatement : StateM Parser (Option Statement) := do
while ! (← get).curTokenIs (Token.SEMICOLON) do
nextToken

-- let ident := Token.IDENT name
return Statement.letStmt name Expression.notImplemented

/-- 一文をパースする -/
Expand Down
25 changes: 22 additions & 3 deletions Monkey/Parser/ParserTest.lean
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ def testLetStatement (stmt : Statement) (expectedId : String) : IO Bool := do

return true

/-- Parser にエラーが一つでもあれば全部出力する -/
def checkParserErrors (p : Parser) : IO Unit := do
if p.errors.isEmpty then
return

for err in p.errors do
IO.eprintln err

throw <| .userError "parser has errors"

/-- 具体的な `let` 文に対する parser のテスト -/
def testLetStatements : IO Unit := do
let input := "
Expand All @@ -32,10 +42,10 @@ def testLetStatements : IO Unit := do
let l := Lexer.new input
let p := Parser.new l

-- none だったときの処理を簡潔に書くことができる
let some program := p.parseProgram |>.fst
| throw <| .userError s!"ParseProgram returned none"
let ⟨result, parser⟩ := p.parseProgram
checkParserErrors parser

let some program := result | IO.eprintln s!"ParseProgram returned none"
IO.println s!"given program={program}"

-- 入力の文はちょうど3つのはず
Expand All @@ -50,4 +60,13 @@ def testLetStatements : IO Unit := do

IO.println "ok!"

/- ## TODO:
入力として以下のように複数のエラーが起こる文を与えたとき、
```
let input := "
let x 5;
let = 10;
let foobar = 838383;"
```
エラーメッセージがなぜ一度しか表示されないのか?-/
#eval testLetStatements

0 comments on commit eb6d379

Please sign in to comment.