Compare commits

..

2 Commits

Author SHA1 Message Date
nub31
0be4e35628 Add check for duplicate local ident 2026-03-05 21:59:24 +01:00
nub31
7dd635fa91 ... 2026-03-05 21:45:04 +01:00
3 changed files with 88 additions and 65 deletions

View File

@@ -167,6 +167,9 @@ public class Generator
static inline void string_rc_dec(struct nub_core_string *string) static inline void string_rc_dec(struct nub_core_string *string)
{ {
if (string == NULL)
return;
if (string->flags & FLAG_STRING_LITERAL) if (string->flags & FLAG_STRING_LITERAL)
return; return;
@@ -362,10 +365,8 @@ public class Generator
if (statement.Value != null) if (statement.Value != null)
{ {
var value = EmitExpression(statement.Value); var value = EmitExpression(statement.Value);
var variableName = TmpName();
writer.WriteLine($"{CType(statement.Value.Type, variableName)} = {value};");
EmitCleanupAllScopes(); EmitCleanupAllScopes();
writer.WriteLine($"return {variableName};"); writer.WriteLine($"return {value};");
} }
else else
{ {
@@ -481,7 +482,7 @@ public class Generator
private string EmitExpression(TypedNodeExpression node) private string EmitExpression(TypedNodeExpression node)
{ {
var value = node switch return node switch
{ {
TypedNodeExpressionBinary expression => EmitExpressionBinary(expression), TypedNodeExpressionBinary expression => EmitExpressionBinary(expression),
TypedNodeExpressionUnary expression => EmitExpressionUnary(expression), TypedNodeExpressionUnary expression => EmitExpressionUnary(expression),
@@ -498,10 +499,6 @@ public class Generator
TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression), TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression),
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null) _ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
}; };
var tmp = TmpName();
writer.WriteLine($"{CType(node.Type, tmp)} = {value};");
return tmp;
} }
private string EmitExpressionBinary(TypedNodeExpressionBinary expression) private string EmitExpressionBinary(TypedNodeExpressionBinary expression)
@@ -509,15 +506,16 @@ public class Generator
var left = EmitExpression(expression.Left); var left = EmitExpression(expression.Left);
var right = EmitExpression(expression.Right); var right = EmitExpression(expression.Right);
var name = Tmp();
if (expression.Operation == TypedNodeExpressionBinary.Op.Add && expression.Left.Type is NubTypeString && expression.Right.Type is NubTypeString) if (expression.Operation == TypedNodeExpressionBinary.Op.Add && expression.Left.Type is NubTypeString && expression.Right.Type is NubTypeString)
{ {
var name = TmpName();
scopes.Peek().DeconstructableNames.Add((name, expression.Type)); scopes.Peek().DeconstructableNames.Add((name, expression.Type));
writer.WriteLine($"{CType(NubTypeString.Instance, name)} = string_concat({left}, {right});"); writer.WriteLine($"{CType(NubTypeString.Instance, name)} = string_concat({left}, {right});");
return name; return name;
} }
return expression.Operation switch var op = expression.Operation switch
{ {
TypedNodeExpressionBinary.Op.Add => $"({left} + {right})", TypedNodeExpressionBinary.Op.Add => $"({left} + {right})",
TypedNodeExpressionBinary.Op.Subtract => $"({left} - {right})", TypedNodeExpressionBinary.Op.Subtract => $"({left} - {right})",
@@ -536,30 +534,40 @@ public class Generator
TypedNodeExpressionBinary.Op.LogicalOr => $"({left} || {right})", TypedNodeExpressionBinary.Op.LogicalOr => $"({left} || {right})",
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
writer.WriteLine($"{CType(expression.Type, name)} = {op};");
return name;
} }
private string EmitExpressionUnary(TypedNodeExpressionUnary expression) private string EmitExpressionUnary(TypedNodeExpressionUnary expression)
{ {
var target = EmitExpression(expression.Target); var target = EmitExpression(expression.Target);
return expression.Operation switch var name = Tmp();
var op = expression.Operation switch
{ {
TypedNodeExpressionUnary.Op.Negate => $"(-{target})", TypedNodeExpressionUnary.Op.Negate => $"(-{target})",
TypedNodeExpressionUnary.Op.Invert => $"(!{target})", TypedNodeExpressionUnary.Op.Invert => $"(!{target})",
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
writer.WriteLine($"{CType(expression.Type, name)} = {op};");
return name;
} }
private string EmitExpressionStringLiteral(TypedNodeExpressionStringLiteral expression) private string EmitExpressionStringLiteral(TypedNodeExpressionStringLiteral expression)
{ {
var name = TmpName(); var name = Tmp();
referencedStringLiterals.Add(name, expression.Value.Value); referencedStringLiterals.Add(name, expression.Value.Value);
return $"&{name}"; return $"&{name}";
} }
private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression) private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression)
{ {
var name = TmpName(); var name = Tmp();
scopes.Peek().DeconstructableNames.Add((name, expression.Type)); scopes.Peek().DeconstructableNames.Add((name, expression.Type));
var initializerValues = new Dictionary<string, string>(); var initializerValues = new Dictionary<string, string>();
@@ -580,7 +588,7 @@ public class Generator
private string EmitExpressionEnumLiteral(TypedNodeExpressionEnumLiteral expression) private string EmitExpressionEnumLiteral(TypedNodeExpressionEnumLiteral expression)
{ {
var name = TmpName(); var name = Tmp();
scopes.Peek().DeconstructableNames.Add((name, expression.Type)); scopes.Peek().DeconstructableNames.Add((name, expression.Type));
var enumVariantType = (NubTypeEnumVariant)expression.Type; var enumVariantType = (NubTypeEnumVariant)expression.Type;
@@ -638,7 +646,10 @@ public class Generator
{ {
var name = EmitExpression(expression.Target); var name = EmitExpression(expression.Target);
var parameterValues = expression.Parameters.Select(EmitExpression).ToList(); var parameterValues = expression.Parameters.Select(EmitExpression).ToList();
return $"{name}({string.Join(", ", parameterValues)})";
var tmp = Tmp();
writer.WriteLine($"{CType(expression.Type, tmp)} = {name}({string.Join(", ", parameterValues)});");
return tmp;
} }
public string CType(NubType node, string? varName = null) public string CType(NubType node, string? varName = null)
@@ -667,7 +678,7 @@ public class Generator
return $"struct {NameMangler.Mangle("anonymous", "struct", type)}{(varName != null ? $" {varName}" : "")}"; return $"struct {NameMangler.Mangle("anonymous", "struct", type)}{(varName != null ? $" {varName}" : "")}";
} }
private string TmpName() private string Tmp()
{ {
return $"_tmp{tmpNameIndex++}"; return $"_tmp{tmpNameIndex++}";
} }

View File

@@ -24,7 +24,7 @@ public class TypeChecker
private readonly NodeDefinitionFunc function; private readonly NodeDefinitionFunc function;
private NubType functionReturnType = null!; private NubType functionReturnType = null!;
private readonly ModuleGraph moduleGraph; private readonly ModuleGraph moduleGraph;
private readonly Scope scope = new(); private readonly Stack<Dictionary<string, NubType>> scopes = new();
private TypedNodeDefinitionFunc? CheckFunction(out List<Diagnostic> diagnostics) private TypedNodeDefinitionFunc? CheckFunction(out List<Diagnostic> diagnostics)
{ {
@@ -51,7 +51,7 @@ public class TypeChecker
} }
} }
using (scope.EnterScope()) using (EnterScope())
{ {
foreach (var parameter in function.Parameters) foreach (var parameter in function.Parameters)
{ {
@@ -60,6 +60,7 @@ public class TypeChecker
try try
{ {
parameterType = ResolveType(parameter.Type); parameterType = ResolveType(parameter.Type);
DeclareLocalIdentifier(parameter.Name, parameterType);
} }
catch (CompileException e) catch (CompileException e)
{ {
@@ -68,7 +69,6 @@ public class TypeChecker
continue; continue;
} }
scope.DeclareIdentifier(parameter.Name.Ident, parameterType);
parameters.Add(new TypedNodeDefinitionFunc.Param(parameter.Tokens, parameter.Name, parameterType)); parameters.Add(new TypedNodeDefinitionFunc.Param(parameter.Tokens, parameter.Name, parameterType));
} }
@@ -114,7 +114,7 @@ public class TypeChecker
private TypedNodeStatementBlock CheckStatementBlock(NodeStatementBlock statement) private TypedNodeStatementBlock CheckStatementBlock(NodeStatementBlock statement)
{ {
using (scope.EnterScope()) using (EnterScope())
{ {
var statements = statement.Statements.Select(CheckStatement).ToList(); var statements = statement.Statements.Select(CheckStatement).ToList();
return new TypedNodeStatementBlock(statement.Tokens, statements); return new TypedNodeStatementBlock(statement.Tokens, statements);
@@ -137,8 +137,19 @@ public class TypeChecker
if (!condition.Type.IsAssignableTo(NubTypeBool.Instance)) if (!condition.Type.IsAssignableTo(NubTypeBool.Instance))
throw BasicError("Condition part of if statement must be a boolean", condition); throw BasicError("Condition part of if statement must be a boolean", condition);
var thenBlock = CheckStatement(statement.ThenBlock); TypedNodeStatement thenBlock;
var elseBlock = statement.ElseBlock == null ? null : CheckStatement(statement.ElseBlock);
using (EnterScope())
{
thenBlock = CheckStatement(statement.ThenBlock);
}
TypedNodeStatement? elseBlock;
using (EnterScope())
{
elseBlock = statement.ElseBlock == null ? null : CheckStatement(statement.ElseBlock);
}
return new TypedNodeStatementIf(statement.Tokens, condition, thenBlock, elseBlock); return new TypedNodeStatementIf(statement.Tokens, condition, thenBlock, elseBlock);
} }
@@ -175,7 +186,7 @@ public class TypeChecker
type ??= value.Type; type ??= value.Type;
scope.DeclareIdentifier(statement.Name.Ident, type); DeclareLocalIdentifier(statement.Name, type);
return new TypedNodeStatementVariableDeclaration(statement.Tokens, statement.Name, type, value); return new TypedNodeStatementVariableDeclaration(statement.Tokens, statement.Name, type, value);
} }
@@ -186,10 +197,12 @@ public class TypeChecker
if (!condition.Type.IsAssignableTo(NubTypeBool.Instance)) if (!condition.Type.IsAssignableTo(NubTypeBool.Instance))
throw BasicError("Condition part of if statement must be a boolean", condition); throw BasicError("Condition part of if statement must be a boolean", condition);
using (EnterScope())
{
var body = CheckStatement(statement.Body); var body = CheckStatement(statement.Body);
return new TypedNodeStatementWhile(statement.Tokens, condition, body); return new TypedNodeStatementWhile(statement.Tokens, condition, body);
} }
}
private TypedNodeStatementMatch CheckStatementMatch(NodeStatementMatch statement) private TypedNodeStatementMatch CheckStatementMatch(NodeStatementMatch statement)
{ {
@@ -214,14 +227,14 @@ public class TypeChecker
uncoveredCases.Remove(@case.Variant.Ident); uncoveredCases.Remove(@case.Variant.Ident);
using (scope.EnterScope()) using (EnterScope())
{ {
if (@case.VariableName != null) if (@case.VariableName != null)
{ {
if (variant.Type is null) if (variant.Type is null)
throw BasicError("Cannot capture variable for enum variant without type", @case.VariableName); throw BasicError("Cannot capture variable for enum variant without type", @case.VariableName);
scope.DeclareIdentifier(@case.VariableName.Ident, variant.Type); DeclareLocalIdentifier(@case.VariableName, variant.Type);
} }
var body = CheckStatement(@case.Body); var body = CheckStatement(@case.Body);
@@ -416,14 +429,14 @@ public class TypeChecker
{ {
if (expression.Sections.Count == 1) if (expression.Sections.Count == 1)
{ {
var name = expression.Sections[0].Ident; var name = expression.Sections[0];
var localType = scope.GetIdentifierType(name); var localType = GetIdentifierType(name.Ident);
if (localType is not null) if (localType is not null)
return new TypedNodeExpressionLocalIdent(expression.Tokens, localType, name); return new TypedNodeExpressionLocalIdent(expression.Tokens, localType, name.Ident);
if (moduleGraph.TryResolveIdentifier(currentModule, name, true, out var ident)) if (moduleGraph.TryResolveIdentifier(currentModule, name.Ident, true, out var ident))
return new TypedNodeExpressionGlobalIdent(expression.Tokens, ident.Type, currentModule, name); return new TypedNodeExpressionGlobalIdent(expression.Tokens, ident.Type, currentModule, name.Ident);
} }
else if (expression.Sections.Count == 2) else if (expression.Sections.Count == 2)
{ {
@@ -755,19 +768,13 @@ public class TypeChecker
return new CompileException(Diagnostic.Error(message).At(fileName, node).Build()); return new CompileException(Diagnostic.Error(message).At(fileName, node).Build());
} }
private sealed class Scope public void DeclareLocalIdentifier(TokenIdent name, NubType type)
{ {
private readonly Stack<Dictionary<string, NubType>> scopes = new(); var existing = GetIdentifierType(name.Ident);
if (existing is not null)
throw BasicError($"Local identifier '{name.Ident}' is already defined", name);
public IDisposable EnterScope() scopes.Peek().Add(name.Ident, type);
{
scopes.Push([]);
return new ScopeGuard(this);
}
public void DeclareIdentifier(string name, NubType type)
{
scopes.Peek().Add(name, type);
} }
public NubType? GetIdentifierType(string name) public NubType? GetIdentifierType(string name)
@@ -783,12 +790,18 @@ public class TypeChecker
return null; return null;
} }
public IDisposable EnterScope()
{
scopes.Push([]);
return new ScopeGuard(this);
}
private void ExitScope() private void ExitScope()
{ {
scopes.Pop(); scopes.Pop();
} }
private sealed class ScopeGuard(Scope owner) : IDisposable private sealed class ScopeGuard(TypeChecker owner) : IDisposable
{ {
public void Dispose() public void Dispose()
{ {
@@ -796,7 +809,6 @@ public class TypeChecker
} }
} }
} }
}
public abstract class TypedNode(List<Token> tokens) public abstract class TypedNode(List<Token> tokens)
{ {

View File

@@ -21,9 +21,9 @@ func main(): i32 {
{ {
core::println("quit") core::println("quit")
} }
Say message Say msg
{ {
core::println(message) core::println(msg)
} }
} }