Skip to content

Commit

Permalink
Can parse and codegen multiple lines of input
Browse files Browse the repository at this point in the history
  • Loading branch information
Craig Stuntz committed Jun 14, 2013
1 parent 0566022 commit f0f004a
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 31 deletions.
29 changes: 19 additions & 10 deletions Lbac.Compiler/CodeGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,35 @@
| Divide -> instruction.Div
| Assign -> failwith "Sorry; no can do"

let rec codegenExpr : Expr -> Try<Method, string> = function
let rec codegenExpr (acc : Method) (expr : Expr) =
match expr with
| Variable v -> Error "Sorry; no can do"
| Invoke m -> Error "Sorry; no can do"
| Minus e ->
match codegenExpr e with
match codegenExpr acc e with
| Success m -> Success({ m with Instructions = m.Instructions @ [Neg] })
| err -> err
| Number n ->
match n with
| 0 -> Success({ Instructions = [Ldc_I4_0]; Locals = [] })
| _ -> Success({ Instructions = [Ldc_I4 n]; Locals = [] })
| 0 -> Success({ acc with Instructions = acc.Instructions @ [Ldc_I4_0] })
| _ -> Success({ acc with Instructions = acc.Instructions @ [Ldc_I4 n] })
| Binary (lhs, oper, rhs) ->
let lhsMethod = codegenExpr lhs
let rhsMethod = codegenExpr rhs
let lhsMethod = codegenExpr { acc with Instructions = [] } lhs
let rhsMethod = codegenExpr { acc with Instructions = [] } rhs
let operInst = codegen_oper oper
match (lhsMethod, rhsMethod) with
| (Success l, Success r) -> Success({ Instructions = List.concat [ l.Instructions; r.Instructions; [operInst] ]; Locals = List.concat [l.Locals; r.Locals] })
| (Success l, Success r) ->
let insts = List.concat [ l.Instructions; r.Instructions; [operInst] ]
let mergeLocals = List.concat [ l.Locals; List.filter (fun i2 -> not (List.exists (fun i1 -> i1 = i2) l.Locals)) r.Locals]
Success({ Instructions = insts; Locals = mergeLocals })
| (Error l, _) -> lhsMethod
| (_, Error r) -> rhsMethod

let rec codegen : Try<Expr list, string> -> Try<Method, string> = function
| Success expList -> codegenExpr expList.Head
| Error(e) -> Error(e)
let rec codegen (expList : ParseResult list) =
let tryCodeGenLine acc line =
match acc, line with
| Success accMethod, Success expr -> codegenExpr accMethod expr
| _, Error err -> Error err
| Error err, _ -> Error err
let emptyMethod = Success( { Instructions = List.empty<instruction>; Locals = List.empty<LocalVar> } )
List.fold tryCodeGenLine emptyMethod expList
17 changes: 7 additions & 10 deletions Lbac.Compiler/Syntax.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
type ParseResult = Try<Expr, string>

/// Converts token list to Success(AST) if valid or Error if not
let parseLine (tokens: Token list): Try<Expr, string> =
let rec parseLine (acc: ParseResult list) (tokens: Token list): ParseResult list =

/// Returns Some(oper) if head of input is a + or - token
let toAddOp = function
Expand Down Expand Up @@ -91,7 +91,7 @@
| _ -> None
| _ -> None

/// expression ::= [addop] term [addop term]* (* unary negation, + not yet implemented *)
/// expression ::= [addop] term [addop term]*
and expression tokens =
match assign tokens with
| Some assignment -> assignment
Expand All @@ -103,16 +103,13 @@
toBinaryExpr(left, Success(addOp), right), rest
| _ -> left, rightTokens

// for the time being we can only parse a single expression
// this will change, but, for now, do that:
let ast, rest = expression tokens

// If anything remains, it's a syntax error
match rest with
| [] -> ast
| wrong :: _ -> Error("Unexpected token: " + (sprintf "%A" wrong))
| [] -> acc @ [ast]
| NewLine _ :: nextLines -> parseLine (acc @ [ast]) nextLines
| wrong :: _ -> [ Error("Unexpected token: " + (sprintf "%A" wrong)) ]

let parse (tokens: Token list): Try<Expr list, string> =
match parseLine tokens with
| Success expr -> Success [expr]
| Error err -> Error err
let parse (tokens: Token list): ParseResult list =
parseLine [] tokens
2 changes: 1 addition & 1 deletion Lbac.Tests/CodeGeneratorTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type CodeGeneratorTests() =
member x.``should codegen 1 + 2 * 0`` () =
let testVal = Expr.Binary(Expr.Binary(Expr.Number(1), Add, Expr.Number(2)), Multiply, Expr.Number(0))
let expected = { Instructions = [Ldc_I4(1); Ldc_I4(2); instruction.Add; Ldc_I4_0; Mul]; Locals = [] }
let actual = CodeGenerator.codegen(Success([testVal]))
let actual = CodeGenerator.codegen([Success(testVal)])
match actual with
| Success il -> Assert.AreEqual(expected, il)
| Error e -> Assert.Fail(e)
Expand Down
26 changes: 16 additions & 10 deletions Lbac.Tests/SyntaxTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,27 @@ open Syntax

[<TestClass>]
type SyntaxTests() =
let shouldFailWith input expected =
let lineShouldFailWith input expected =
let actual = Syntax.parse(input)
match actual with
| Success _ -> Assert.Fail("Expected " + expected)
| Error e -> Assert.AreEqual(expected, e)
let isMatch = function
| Error e -> expected = e
| _ -> false
if not (List.exists isMatch actual) then Assert.Fail("Expected " + expected)

let shouldParseTo input expected =
let actual = Syntax.parse(input)
match actual with
| Success parsed -> Assert.AreEqual(expected, parsed)
| Error e -> Assert.Fail e
let itemMatches exp = function
| Success parsed -> Assert.AreEqual(exp, parsed)
| Error e -> Assert.Fail e
List.iter (fun (e, a) -> itemMatches e a) (List.zip expected actual)

[<TestMethod>]
member x.``should parse 11`` () =
[Token.Number(11)] |> shouldParseTo <| [ Expr.Number(11) ]

[<TestMethod>]
member x.``should error on garbage`` () =
[Symbol('x')] |> shouldFailWith <| "Identifier expected"
[Symbol('x')] |> lineShouldFailWith <| "Identifier expected"

[<TestMethod>]
member x.``should parse 11 + 22`` () =
Expand All @@ -39,8 +41,8 @@ type SyntaxTests() =
[<TestMethod>]
member x.``(10 - 2 * 3 should fail with mismatched (`` () =
[Symbol('('); Token.Number(10); Symbol('-'); Token.Number(2); Symbol('*'); Token.Number(3)]
|> shouldFailWith <| "')' expected."

|> lineShouldFailWith <| "')' expected."
[<TestMethod>]
member x.``should parse -1`` () =
[Symbol('-'); Token.Number(1)] |> shouldParseTo <| [ Expr.Minus(Expr.Number(1)) ]
Expand All @@ -58,3 +60,7 @@ type SyntaxTests() =
[<TestMethod>]
member x.``should parse x = 1``() =
[Identifier("x"); Symbol('='); Token.Number(1)] |> shouldParseTo <| [ Expr.Binary(Expr.Variable("x"), Operator.Assign, Expr.Number(1)) ]

[<TestMethod>]
member x.``should parse multiple lines``() =
[Token.Number(1); NewLine; Token.Number(2)] |> shouldParseTo <| [ Expr.Number(1); Expr.Number(2) ]

0 comments on commit f0f004a

Please sign in to comment.