Skip to content
This repository was archived by the owner on Jan 11, 2026. It is now read-only.

Commit c34072f

Browse files
committed
analyzer
1 parent 11d5953 commit c34072f

File tree

18 files changed

+2072
-200
lines changed

18 files changed

+2072
-200
lines changed

compiler/Program.cs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Text;
22
using System.Text.Json;
33
using System.Text.Json.Serialization;
4+
using MiniLang.Semantic;
5+
using MiniLang.Syntax;
46

57
public class Program
68
{
@@ -81,21 +83,62 @@ public static void Main(string[] args)
8183

8284
try
8385
{
84-
var tokens = new Lexer(sourceCode).ScanTokens();
85-
var analyzer = new Analyzer();
86-
var ast = analyzer.analyze(tokens);
8786
var options = new JsonSerializerOptions
8887
{
8988
Converters = { new JsonStringEnumConverter() },
90-
MaxDepth = 2048
89+
MaxDepth = 2048,
90+
WriteIndented = false
9191
};
92-
var json = JsonSerializer.Serialize(ast, options);
93-
writer.WriteLine(json);
94-
}
95-
catch (SyntaxError e)
96-
{
97-
Console.Error.WriteLine(e.Message);
98-
Environment.ExitCode = 1;
92+
93+
IReadOnlyList<Token> tokens;
94+
try
95+
{
96+
tokens = new Lexer(sourceCode).ScanTokens();
97+
}
98+
catch (SyntaxError le)
99+
{
100+
var lexErr = new Diagnostic(Stage.Lex, le.Line, le.Message, Severity.Error);
101+
var output = new PipelineOutput(Array.Empty<Token>(), null, new SemanticReport(Array.Empty<Diagnostic>(), Array.Empty<Diagnostic>()), null, lexErr, null, null);
102+
writer.WriteLine(JsonSerializer.Serialize(output, options));
103+
return;
104+
}
105+
106+
ProgramAst? ast = null;
107+
try
108+
{
109+
var analyzer = new Analyzer();
110+
ast = analyzer.analyze(tokens);
111+
}
112+
catch (SyntaxError pe)
113+
{
114+
var parseErr = new Diagnostic(Stage.Parse, pe.Line, pe.Message, Severity.Error);
115+
var output = new PipelineOutput(tokens, null, new SemanticReport(Array.Empty<Diagnostic>(), Array.Empty<Diagnostic>()), null, parseErr, null, null);
116+
writer.WriteLine(JsonSerializer.Serialize(output, options));
117+
return;
118+
}
119+
120+
var sema = new SemanticAnalyzer();
121+
var report = sema.Analyze(ast, sourceCode);
122+
123+
ProgramAst? optimized = null;
124+
List<Optimizer.OptimizationStep>? steps = null;
125+
string? optimizedSource = null;
126+
try
127+
{
128+
var res = Optimizer.OptimizeWithReport(ast);
129+
optimized = res.Program;
130+
steps = res.Steps;
131+
optimizedSource = optimized != null ? CodePrinter.PrintProgram(optimized) : null;
132+
}
133+
catch
134+
{
135+
optimized = null;
136+
steps = null;
137+
optimizedSource = null;
138+
}
139+
140+
var result = new PipelineOutput(tokens, ast, report, optimized, null, steps, optimizedSource);
141+
writer.WriteLine(JsonSerializer.Serialize(result, options));
99142
}
100143
catch (Exception e)
101144
{

compiler/analyzer/Analyzer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ private Statement ParseStatement()
219219
if (Current.Type == TokenType.While) return ParseWhile();
220220
if (Current.Type == TokenType.If) return ParseIf();
221221
if (Current.Type == TokenType.Return) return ParseReturn();
222+
if (Current.Type == TokenType.Break) return ParseBreak();
222223
var lhs = ParseExpression();
223224
if (Match(TokenType.ColonEqual))
224225
{
@@ -264,6 +265,12 @@ private ReturnStmt ParseReturn()
264265
return new ReturnStmt(expr, r.Line);
265266
}
266267

268+
private BreakStmt ParseBreak()
269+
{
270+
var b = Consume(TokenType.Break, "'break'");
271+
return new BreakStmt(b.Line);
272+
}
273+
267274
private Expression ParseExpression() => ParsePostfix();
268275

269276
private Expression ParsePostfix()

compiler/analyzer/Ast.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public sealed record ProgramAst(IReadOnlyList<TopLevelNode> Items) : AstNode(0);
1515
[JsonDerivedType(typeof(WhileStmt), "while")]
1616
[JsonDerivedType(typeof(IfStmt), "if")]
1717
[JsonDerivedType(typeof(ReturnStmt), "return")]
18+
[JsonDerivedType(typeof(BreakStmt), "break")]
1819
public abstract record TopLevelNode(int Line) : AstNode(Line);
1920

2021
[JsonPolymorphic(TypeDiscriminatorPropertyName = "stmt")]
@@ -24,6 +25,7 @@ public abstract record TopLevelNode(int Line) : AstNode(Line);
2425
[JsonDerivedType(typeof(WhileStmt), "while")]
2526
[JsonDerivedType(typeof(IfStmt), "if")]
2627
[JsonDerivedType(typeof(ReturnStmt), "return")]
28+
[JsonDerivedType(typeof(BreakStmt), "break")]
2729
public abstract record Statement(int Line) : TopLevelNode(Line);
2830

2931
[JsonPolymorphic(TypeDiscriminatorPropertyName = "expr")]
@@ -54,6 +56,7 @@ public sealed record ExprStmt(Expression Expr, int Line) : Statement(Line);
5456
public sealed record IfStmt(Expression Condition, Statement Then, Statement? Else, int Line) : Statement(Line);
5557
public sealed record WhileStmt(Expression Condition, IReadOnlyList<Statement> Body, int Line) : Statement(Line);
5658
public sealed record ReturnStmt(Expression? Expr, int Line) : Statement(Line);
59+
public sealed record BreakStmt(int Line) : Statement(Line);
5760

5861
[JsonPolymorphic(TypeDiscriminatorPropertyName = "member")]
5962
[JsonDerivedType(typeof(FieldDecl), "field")]

compiler/lexer/TokenSet.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static IReadOnlyList<ITokenDefinition> Default() =>
2828
{"then", TokenType.Then},
2929
{"else", TokenType.Else},
3030
{"return", TokenType.Return},
31+
{"break", TokenType.Break},
3132
{"this", TokenType.This},
3233
{"true", TokenType.True},
3334
{"false", TokenType.False}

compiler/lexer/TokenType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ public enum TokenType
3434
False = Boolean * 127, // false
3535
Eof = 131, // end of file
3636
LineComment = 137, // //
37+
Break = 149, // break
3738
}

compiler/semantic/AstUtils.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using MiniLang.Syntax;
2+
3+
namespace MiniLang.Semantic;
4+
5+
public static class AstUtils
6+
{
7+
public static T DeepClone<T>(T node) where T : AstNode => (T)CloneAst(node);
8+
9+
public static TypeRef CloneTypeRef(TypeRef t)
10+
=> new TypeRef(t.Name, t.TypeArguments.Select(CloneTypeRef).ToList(), t.Line);
11+
12+
private static AstNode CloneAst(AstNode node)
13+
{
14+
switch (node)
15+
{
16+
case ProgramAst p:
17+
return new ProgramAst(p.Items.Select(i => (TopLevelNode)CloneAst(i)).ToList());
18+
case ClassDecl c:
19+
return new ClassDecl(c.Name, c.TypeParameters.ToList(), c.BaseType != null ? CloneTypeRef(c.BaseType) : null,
20+
c.Members.Select(m => (ClassMember)CloneAst(m)).ToList(), c.Line);
21+
case FieldDecl f:
22+
return new FieldDecl(f.Name, (Expression)CloneAst(f.Init), f.Line);
23+
case MethodDecl m:
24+
return new MethodDecl(m.Name, m.Parameters.Select(p => new Parameter(p.Name, CloneTypeRef(p.Type), p.Line)).ToList(),
25+
m.ReturnType != null ? CloneTypeRef(m.ReturnType) : null,
26+
m.Body != null ? (MethodBody)CloneAst(m.Body) : null,
27+
m.IsConstructor, m.Line);
28+
case BlockBody b:
29+
return new BlockBody(b.Statements.Select(s => (Statement)CloneAst(s)).ToList(), b.Line);
30+
case ExprBody eb:
31+
return new ExprBody((Expression)CloneAst(eb.Expr), eb.Line);
32+
case VarDeclStmt v:
33+
return new VarDeclStmt(v.Name, (Expression)CloneAst(v.Init), v.Line);
34+
case AssignStmt a:
35+
return new AssignStmt((Expression)CloneAst(a.Target), (Expression)CloneAst(a.Value), a.Line);
36+
case ExprStmt es:
37+
return new ExprStmt((Expression)CloneAst(es.Expr), es.Line);
38+
case IfStmt i:
39+
return new IfStmt((Expression)CloneAst(i.Condition), (Statement)CloneAst(i.Then), i.Else != null ? (Statement)CloneAst(i.Else) : null, i.Line);
40+
case WhileStmt w:
41+
return new WhileStmt((Expression)CloneAst(w.Condition), w.Body.Select(s => (Statement)CloneAst(s)).ToList(), w.Line);
42+
case ReturnStmt r:
43+
return new ReturnStmt(r.Expr != null ? (Expression)CloneAst(r.Expr) : null, r.Line);
44+
case BreakStmt br:
45+
return new BreakStmt(br.Line);
46+
case IdentifierExpr ie:
47+
return new IdentifierExpr(ie.Name, ie.Line);
48+
case ThisExpr th:
49+
return new ThisExpr(th.Line);
50+
case LiteralExpr le:
51+
return new LiteralExpr(le.Lexeme, le.Kind, le.Line);
52+
case MemberAccessExpr ma:
53+
return new MemberAccessExpr((Expression)CloneAst(ma.Target), ma.Member, ma.Line);
54+
case CallExpr call:
55+
return new CallExpr((Expression)CloneAst(call.Target), call.Arguments.Select(a => (Expression)CloneAst(a)).ToList(), call.Line);
56+
case GenericRefExpr gr:
57+
return new GenericRefExpr((Expression)CloneAst(gr.Target), gr.TypeArguments.Select(CloneTypeRef).ToList(), gr.Line);
58+
case IndexExpr ix:
59+
return new IndexExpr((Expression)CloneAst(ix.Target), (Expression)CloneAst(ix.Index), ix.Line);
60+
case ParenExpr pe:
61+
return new ParenExpr((Expression)CloneAst(pe.Inner), pe.Line);
62+
default:
63+
throw new NotSupportedException($"Unknown AST node: {node.GetType().Name}");
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)