Skip to content

Commit 4cb0d4d

Browse files
Ex10si0nzhiyuuuuEdwinaZhuygrx532
authored
Add TypeScript Strata features: while, for, switch, string, array basics, arrow functions (#168)
Created a linear commit history --------- Signed-off-by: YingZhu <[email protected]> Co-authored-by: zhiyuuuu <[email protected]> Co-authored-by: YingZhu <[email protected]> Co-authored-by: ygrx532 <[email protected]>
1 parent b0346c4 commit 4cb0d4d

File tree

6 files changed

+180
-90
lines changed

6 files changed

+180
-90
lines changed

.gitignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,5 @@ vcs/*.smt2
66

77
Strata.code-workspace
88

9-
conformance_testing/failures/*
10-
package.json
11-
package-lock.json
12-
139
conformance_testing/__pycache__
1410
conformance_testing/failures

Strata/Languages/TypeScript/TS_to_Strata.lean

Lines changed: 105 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ private def isBareContinueStmt (s : TS_Statement) : Bool :=
4848
private def isIfWithBareContinue (s : TS_Statement) : Option TS_IfStatement :=
4949
match s with
5050
| .TS_IfStatement ifs =>
51-
let conseqIsBare :=
52-
match ifs.consequent with
53-
| .TS_ContinueStatement _ => true
54-
| .TS_BlockStatement b =>
55-
-- exactly one statement and it is `continue`
56-
match b.body.toList with
57-
| [one] => isBareContinueStmt one
58-
| _ => false
59-
| _ => false
60-
if conseqIsBare && ifs.alternate.isNone then some ifs else none
51+
let conseqIsBare :=
52+
match ifs.consequent with
53+
| .TS_ContinueStatement _ => true
54+
| .TS_BlockStatement b =>
55+
-- exactly one statement and it is `continue`
56+
match b.body.toList with
57+
| [one] => isBareContinueStmt one
58+
| _ => false
59+
| _ => false
60+
if conseqIsBare && ifs.alternate.isNone then some ifs else none
6161
| _ => none
6262

6363
def TS_type_to_HMonoTy (ty: String) : Heap.HMonoTy :=
@@ -224,19 +224,19 @@ partial def translate_statement_core
224224
(ct: ControlTargets := {}) : TranslationContext × List TSStrataStatement :=
225225
match s with
226226
| .TS_FunctionDeclaration funcDecl =>
227-
-- Translate function definition
228-
dbg_trace s!"[DEBUG] Translating TypeScript function definition: {funcDecl.id.name}"
229-
dbg_trace s!"[DEBUG] Function parameters: {funcDecl.params.toList.map (·.name)}"
230-
dbg_trace s!"[DEBUG] Function body has statements"
227+
-- Translate function definition
228+
dbg_trace s!"[DEBUG] Translating TypeScript function definition: {funcDecl.id.name}"
229+
dbg_trace s!"[DEBUG] Function parameters: {funcDecl.params.toList.map (·.name)}"
230+
dbg_trace s!"[DEBUG] Function body has statements"
231231

232-
let (bodyCtx, funcBody) := match funcDecl.body with
232+
let (bodyCtx, funcBody) := match funcDecl.body with
233233
| .TS_BlockStatement blockStmt =>
234234
-- Thread context through function body to handle nested functions
235235
blockStmt.body.toList.foldl
236236
(fun (accCtx, accStmts) stmt =>
237237
let (newCtx, stmts) := translate_statement_core stmt accCtx ct
238-
(newCtx, accStmts ++ stmts))
239-
(ctx, [])
238+
(newCtx, accStmts ++ stmts))
239+
(ctx, [])
240240
| _ => panic! s!"Expected block statement as function body, got: {repr funcDecl.body}"
241241

242242
dbg_trace s!"[DEBUG] Translated function body to {funcBody.length} Strata statements"
@@ -454,28 +454,28 @@ partial def translate_statement_core
454454

455455
let isIfWithBareContinue : TS_Statement → Option TS_IfStatement
456456
| .TS_IfStatement ifs =>
457-
let conseqIsBare :=
458-
match ifs.consequent with
459-
| .TS_ContinueStatement _ => true
460-
| .TS_BlockStatement b =>
461-
match b.body.toList with
462-
| [one] => isBareContinueStmt one
463-
| _ => false
464-
| _ => false
465-
if conseqIsBare && ifs.alternate.isNone then some ifs else none
457+
let conseqIsBare :=
458+
match ifs.consequent with
459+
| .TS_ContinueStatement _ => true
460+
| .TS_BlockStatement b =>
461+
match b.body.toList with
462+
| [one] => isBareContinueStmt one
463+
| _ => false
464+
| _ => false
465+
if conseqIsBare && ifs.alternate.isNone then some ifs else none
466466
| _ => none
467467

468468
let isIfWithBareBreak : TS_Statement → Option TS_IfStatement
469469
| .TS_IfStatement ifs =>
470-
let conseqIsBare :=
471-
match ifs.consequent with
472-
| .TS_BreakStatement _ => true
473-
| .TS_BlockStatement b =>
474-
match b.body.toList with
475-
| [one] => isBareBreakStmt one
476-
| _ => false
477-
| _ => false
478-
if conseqIsBare && ifs.alternate.isNone then some ifs else none
470+
let conseqIsBare :=
471+
match ifs.consequent with
472+
| .TS_BreakStatement _ => true
473+
| .TS_BlockStatement b =>
474+
match b.body.toList with
475+
| [one] => isBareBreakStmt one
476+
| _ => false
477+
| _ => false
478+
if conseqIsBare && ifs.alternate.isNone then some ifs else none
479479
| _ => none
480480

481481
-- when we hit the pattern (in a loop), guard the tail with `else`
@@ -493,8 +493,8 @@ partial def translate_statement_core
493493
let (tailCtx, tailStmts) :=
494494
tail.foldl
495495
(fun (p, accS) stmt =>
496-
let (p2, ss2) := translate_statement_core stmt p ct
497-
(p2, accS ++ ss2))
496+
let (p2, ss2) := translate_statement_core stmt p ct
497+
(p2, accS ++ ss2))
498498
(accCtx, [])
499499
let cond := translate_expr ifs.test
500500
let thenBlk : Imperative.Block TSStrataExpression TSStrataCommand := { ss := [] }
@@ -506,8 +506,8 @@ partial def translate_statement_core
506506
let (tailCtx, tailStmts) :=
507507
tail.foldl
508508
(fun (p, accS) stmt =>
509-
let (p2, ss2) := translate_statement_core stmt p ct
510-
(p2, accS ++ ss2))
509+
let (p2, ss2) := translate_statement_core stmt p ct
510+
(p2, accS ++ ss2))
511511
(accCtx, [])
512512
let cond := translate_expr ifs.test
513513
let setBreakFlag : TSStrataStatement := .cmd (.set breakFlagVar Heap.HExpr.true)
@@ -558,8 +558,8 @@ partial def translate_statement_core
558558
(bodyCtx, [ initBreakFlag, .loop combinedCondition none none bodyBlock ])
559559

560560
| .TS_ForStatement forStmt =>
561-
562561
dbg_trace s!"[DEBUG] Translating for statement at loc {forStmt.start_loc}-{forStmt.end_loc}"
562+
563563
let continueLabel := s!"for_continue_{forStmt.start_loc}"
564564
let breakLabel := s!"for_break_{forStmt.start_loc}"
565565
let breakFlagVar := s!"for_break_flag_{forStmt.start_loc}"
@@ -571,12 +571,10 @@ partial def translate_statement_core
571571
let (_, initStmts) := translate_statement_core (.TS_VariableDeclaration forStmt.init) ctx
572572
-- guard (test)
573573
let guard := translate_expr forStmt.test
574-
575574
-- body (first translate loop body with break support)
576575
let (ctx1, bodyStmts) :=
577576
translate_statement_core forStmt.body ctx
578577
{ continueLabel? := some continueLabel, breakLabel? := some breakLabel, breakFlagVar? := some breakFlagVar }
579-
580578
-- update (translate expression into statements following ExpressionStatement style)
581579
let (_, updateStmts) :=
582580
translate_statement_core
@@ -599,31 +597,75 @@ partial def translate_statement_core
599597
-- output: init break flag, init statements, then a loop statement
600598
(ctx1, [initBreakFlag] ++ initStmts ++ [ .loop combinedCondition none none loopBody])
601599

602-
| .TS_ContinueStatement cont =>
603-
let tgt :=
604-
match ct.continueLabel? with
605-
| some lab => lab
606-
| none =>
607-
dbg_trace "[WARN] `continue` encountered outside of a loop; emitting goto to __unbound_continue";
608-
"__unbound_continue"
609-
(ctx, [ .goto tgt ])
610-
611-
| .TS_BreakStatement brk =>
612-
-- Handle break statement if loop context fails to handle it
613-
dbg_trace "[WARN] `break` statement not handled by pattern matching";
614-
match ct.breakFlagVar? with
615-
| some flagVar =>
616-
-- Set break flag to true as fallback
617-
(ctx, [ .cmd (.set flagVar Heap.HExpr.true) ])
618-
| none =>
619-
dbg_trace "[WARN] `break` encountered outside of a loop; using fallback goto";
600+
| .TS_SwitchStatement switchStmt =>
601+
-- Handle switch statement: switch discriminant { cases }
602+
603+
-- Process all cases in their original order, separating regular from default
604+
let allCases := switchStmt.cases.toList
605+
let (regularCaseStmts, defaultStmts) := allCases.foldl (fun (regCases, defStmts) case =>
606+
match case.test with
607+
| some expr =>
608+
-- Regular case
609+
let discrimExpr := translate_expr switchStmt.discriminant
610+
let caseValue := translate_expr expr
611+
let testExpr := Heap.HExpr.app (Heap.HExpr.app (Heap.HExpr.deferredOp "Int.Eq" none) discrimExpr) caseValue
612+
let (caseCtx, stmts) := case.consequent.foldl (fun (accCtx, accStmts) stmt =>
613+
let (newCtx, newStmts) := translate_statement_core stmt accCtx
614+
(newCtx, accStmts ++ newStmts)) (ctx, [])
615+
(regCases ++ [(testExpr, stmts)], defStmts)
616+
| none =>
617+
-- Default case
618+
let (defaultCtx, stmts) := case.consequent.foldl (fun (accCtx, accStmts) stmt =>
619+
let (newCtx, newStmts) := translate_statement_core stmt accCtx
620+
(newCtx, accStmts ++ newStmts)) (ctx, [])
621+
(regCases, stmts)
622+
) ([], [])
623+
624+
-- Build nested if-then-else structure for regular cases
625+
let rec build_cases (cases: List (Heap.HExpr × List TSStrataStatement)) (defaultStmts: List TSStrataStatement) : TSStrataStatement :=
626+
match cases with
627+
| [] =>
628+
-- No regular cases, just execute default if it exists
629+
let defaultBlock : Imperative.Block TSStrataExpression TSStrataCommand := { ss := defaultStmts }
630+
.block "default" defaultBlock
631+
| [(test, stmts)] =>
632+
let thenBlock : Imperative.Block TSStrataExpression TSStrataCommand := { ss := stmts }
633+
let elseBlock : Imperative.Block TSStrataExpression TSStrataCommand := { ss := defaultStmts }
634+
.ite test thenBlock elseBlock
635+
| (test, stmts) :: rest =>
636+
let thenBlock : Imperative.Block TSStrataExpression TSStrataCommand := { ss := stmts }
637+
let elseBlock := build_cases rest defaultStmts
638+
let elseBlockWrapped : Imperative.Block TSStrataExpression TSStrataCommand := { ss := [elseBlock] }
639+
.ite test thenBlock elseBlockWrapped
640+
641+
let switchStructure := build_cases regularCaseStmts defaultStmts
642+
(ctx, [switchStructure])
643+
644+
| .TS_ContinueStatement cont =>
620645
let tgt :=
621-
match ct.breakLabel? with
646+
match ct.continueLabel? with
622647
| some lab => lab
623-
| none => "__unbound_break"
648+
| none =>
649+
dbg_trace "[WARN] `continue` encountered outside of a loop; emitting goto to __unbound_continue";
650+
"__unbound_continue"
624651
(ctx, [ .goto tgt ])
625652

626-
| _ => panic! s!"Unimplemented statement: {repr s}"
653+
| .TS_BreakStatement brk =>
654+
-- Handle break statement if loop context fails to handle it
655+
dbg_trace "[WARN] `break` statement not handled by pattern matching";
656+
match ct.breakFlagVar? with
657+
| some flagVar =>
658+
-- Set break flag to true as fallback
659+
(ctx, [ .cmd (.set flagVar Heap.HExpr.true) ])
660+
| none =>
661+
dbg_trace "[WARN] `break` encountered outside of a loop; using fallback goto";
662+
let tgt :=
663+
match ct.breakLabel? with
664+
| some lab => lab
665+
| none => "__unbound_break"
666+
(ctx, [ .goto tgt ])
667+
668+
| _ => panic! s!"Unimplemented statement: {repr s}"
627669

628670
-- Translate TypeScript statements to TypeScript-Strata statements
629671
partial def translate_statement (s: TS_Statement) (ctx : TranslationContext) : TranslationContext × List TSStrataStatement :=

Strata/Languages/TypeScript/js_ast.lean

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ mutual
4848
| TS_TSArrayType : TS_TSArrayType → TS_TSTypeKeyword
4949
deriving Repr, Lean.FromJson, Lean.ToJson
5050

51-
-- TODO: Array not as a type?
5251
structure TS_TSArrayType extends BaseNode where
5352
elementType : TS_TSTypeKeyword
5453
deriving Repr, Lean.FromJson, Lean.ToJson
54+
55+
-- TODO: Array not as a type?
56+
-- structure TS_TSArrayType extends BaseNode where
57+
-- elementType : TS_TSTypeKeyword
58+
-- deriving Repr, Lean.FromJson, Lean.ToJson
5559
end
5660

5761
structure TS_TSTypeAnnotation extends BaseNode where
@@ -221,6 +225,14 @@ mutual
221225
alternate : Option TS_Statement
222226
deriving Repr, Lean.FromJson, Lean.ToJson
223227

228+
/-- A single `case` (or `default` when `test = none`) inside a switch. -/
229+
structure TS_SwitchCase extends BaseNode where
230+
/-- `some expr` for `case expr:`, `none` for `default:` --/
231+
test : Option TS_Expression
232+
/-- statements executed for this case (often end with a BreakStatement) --/
233+
consequent : Array TS_Statement
234+
deriving Repr, Lean.FromJson, Lean.ToJson
235+
224236
structure TS_ReturnStatement extends BaseNode where
225237
argument : Option TS_Expression
226238
deriving Repr, Lean.FromJson, Lean.ToJson
@@ -254,6 +266,12 @@ mutual
254266
label : Option TS_Identifier := none
255267
deriving Repr, Lean.FromJson, Lean.ToJson
256268

269+
/-- `switch (discriminant) { cases... }` -/
270+
structure TS_SwitchStatement extends BaseNode where
271+
discriminant : TS_Expression
272+
cases : Array TS_SwitchCase
273+
deriving Repr, Lean.FromJson, Lean.ToJson
274+
257275
inductive TS_Statement where
258276
| TS_IfStatement : TS_IfStatement → TS_Statement
259277
| TS_VariableDeclaration : TS_VariableDeclaration → TS_Statement
@@ -262,10 +280,11 @@ mutual
262280
| TS_ThrowStatement : TS_ThrowStatement → TS_Statement
263281
| TS_ReturnStatement : TS_ReturnStatement → TS_Statement
264282
| TS_FunctionDeclaration : TS_FunctionDeclaration → TS_Statement
265-
| TS_WhileStatement: TS_WhileStatement → TS_Statement
266-
| TS_ContinueStatement: TS_ContinueStatement → TS_Statement
267-
| TS_BreakStatement: TS_BreakStatement → TS_Statement
268283
| TS_ForStatement : TS_ForStatement → TS_Statement
284+
| TS_WhileStatement: TS_WhileStatement -> TS_Statement
285+
| TS_BreakStatement : TS_BreakStatement → TS_Statement
286+
| TS_SwitchStatement : TS_SwitchStatement → TS_Statement
287+
| TS_ContinueStatement: TS_ContinueStatement -> TS_Statement
269288
deriving Repr, Lean.FromJson, Lean.ToJson
270289
end
271290

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function getWeekendName(date: number): string | undefined {
2+
switch (date) {
3+
case 0:
4+
return "Sunday";
5+
case 6:
6+
return "Saturday";
7+
}
8+
}
9+
10+
let day: number = 6;
11+
let weekendName: string | undefined = getWeekendName(day);
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
let i:number = 0;
2-
while (i < 1) {
3-
i = 3;
1+
let i: number = 0;
2+
while (i < 5) {
3+
i = i + 1;
44
}

0 commit comments

Comments
 (0)