void CompileCall(CallExpression call) { // Try to resolve the function Symbol sym; var callNameExpr = call.Target as NameExpression; if (callNameExpr != null) { // Check if the target function is in scope if (!Table.FindSymbol(callNameExpr.Name, out sym)) { // The function is undefined or not in scope // Throw an exception throw LoreException.Create(Location).Describe($"Attempt to call undefined function '{callNameExpr.Name}'"); } // Parse the arguments var args = new LLVMValueRef [Math.Max(call.Arguments.Count, 0)]; for (var i = 0; i < args.Length; i++) { call.Arguments.Arguments [i].Visit(this); args [i] = Stack.Pop().Value; } // The function is in scope var callResult = LLVM.BuildCall(Builder, sym.Value, args, "tmpcall"); Stack.Push(Symbol.CreateAnonymous(callResult)); } else { throw LoreException.Create(Location).Describe($"Attempt to call something that is everything but a function."); } }
/// <summary> /// Pops a scope. /// </summary> /// <returns>The scope.</returns> public void PopScope() { if (Scopes.Count <= 1) { throw LoreException.Create().Describe("Attempt to leave global scope.").Describe("This is a compiler bug."); } Scopes.Pop(); }
AssignStatement ParseAssignStatement() { var stmt = AssignStatement.Create(unit.Location); // Parse identifiers while (!unit.Match(LoreToken.Operator, "=")) { // Parse name var ident = ParseName(); var name = NamedParameter.Create(ident); // Parse type if (unit.Accept(LoreToken.Colon)) { name.SetType(ParseName()); } // Add the named parameter to the identifiers stmt.AddIdentifier(name); // Try parsing another identifier if (!unit.Match(LoreToken.Operator, "=")) { unit.Expect(LoreToken.Comma); } } unit.Expect(LoreToken.Operator, "="); // Parse expressions do { var expr = ParseExpression(); stmt.AddExpression(expr); } while (unit.Accept(LoreToken.Comma)); // Verify that all identifiers are satisfied var pluralizeExpression = stmt.ExpressionCount > 1 ? "s" : string.Empty; var pluralizeIdentifier = stmt.IdentifierCount > 1 ? "s" : string.Empty; if (stmt.IdentifierCount > stmt.ExpressionCount) { var count = stmt.IdentifierCount - stmt.ExpressionCount; var pluralizeDifference = count > 1 ? "s" : string.Empty; throw LoreException.Create(stmt.Location) .Describe($"Attempt to assign {stmt.ExpressionCount} expression{pluralizeExpression} to {stmt.IdentifierCount} identifier{pluralizeIdentifier}.") .Resolve($"Add more expressions or remove {count} identifier{pluralizeDifference}."); } if (stmt.IdentifierCount < stmt.ExpressionCount) { var count = stmt.ExpressionCount - stmt.IdentifierCount; var pluralizeDifference = count > 1 ? "s" : string.Empty; throw LoreException.Create(stmt.Location) .Describe($"Attempt to assign {stmt.ExpressionCount} expression{pluralizeExpression} to {stmt.IdentifierCount} identifier{pluralizeIdentifier}.") .Resolve($"Discard {count} expression{pluralizeDifference} by using the _ operator."); } return(stmt); }
public void Capture(string name) { if (Captures.Contains(name)) { throw LoreException.Create() .Describe($"Symbol '{name}' was captured multiple times.") .Resolve($"Capture the symbol just once."); } Captures.Add(name); }
/// <summary> /// Create a new instance of the <see cref="Capture"/> class. /// </summary> /// <param name="unit">Unit.</param> /// <param name="lex">Lex.</param> public static Capture Create(ParsingUnit unit, Lexeme <LoreToken> lex) { if (lex.Is(LoreToken.Identifier)) { return(new Capture(lex.Value, capturesAll: false)); } if (lex.Is(LoreToken.Operator) && lex.Is("=")) { return(new Capture(lex.Value, capturesAll: true)); } // Invalid capture throw LoreException.Create(unit.Location).Describe($"Not a capture: '{lex.Value}' ({lex.Token})"); }
IntegerExpression ParseInteger() { var expr = IntegerExpression.Create(unit.Location); var literal = unit.Expect(LoreToken.IntLiteral).Value; // Try parsing the string ulong intval; try { intval = ulong.Parse(literal, NumberStyles.Integer); } catch (Exception e) { throw LoreException.Create(unit.Location).Describe($"Failed to parse integer literal: ${e.Message}"); } // Determine the size of the integer expr.SetSize(IntegerSize.ULong); if (intval <= (byte)sbyte.MaxValue) { expr.SetSize(IntegerSize.SByte); } else if (intval <= byte.MaxValue) { expr.SetSize(IntegerSize.UByte); } else if (intval <= (ushort)short.MaxValue) { expr.SetSize(IntegerSize.SShort); } else if (intval <= ushort.MaxValue) { expr.SetSize(IntegerSize.UShort); } else if (intval <= int.MaxValue) { expr.SetSize(IntegerSize.SWord); } else if (intval <= uint.MaxValue) { expr.SetSize(IntegerSize.UWord); } else if (intval <= long.MaxValue) { expr.SetSize(IntegerSize.SLong); } // Set the value and return expr.SetValue(intval); return(expr); }
void CompileAssignment(AssignStatement stmt, NamedParameter identifier, AstNode expr) { // Check if the variable already exists in the parent scope Symbol sym; if (Table.TopScope.FindSymbol(identifier.Name.Name, out sym)) { // The variable already exists throw LoreException.Create(Location) .Describe($"Redefinition of variable '{identifier.Name.Name}'.") .Describe($"Previous definition was at '{sym.Location}'."); } // Compile the expression expr.Visit(this); // Get the expression value and type var exprVal = Stack.Pop().Value; var exprValType = exprVal.TypeOf(); // Check if the identifier has a type if (identifier.HasType) { var identType = Helper.GetBuiltinTypeFromString(identifier.Type.Name); // Check if the type of the identifier is different // from the type of the expression if (!Helper.CompareType(identType, exprValType)) { // Try casting the expression value to the expected type exprVal = Helper.BuildCast(Builder, exprVal, identType); } // Set the type of the expression to the type of the identifier exprValType = identType; } // Allocate the variable var ptr = LLVM.BuildAlloca(Builder, exprValType, identifier.Name.Name); // Store the value in the variable LLVM.BuildStore(Builder, exprVal, ptr); // Add the variable to the symbol table var name = identifier.Name.Name; sym = Symbol.CreatePtr(name, ptr, Location, stmt.Mutable); Table.AddSymbol(sym); }
public LLVMTypeRef GetBuiltinTypeFromString(string type) { switch (type) { case "void": return(VoidType); case "bool": return(BoolType); case "char": case "int8": return(Int8Type); case "uint8": return(UInt8Type); case "int16": return(Int16Type); case "uint16": return(UInt16Type); case "int": case "int32": return(Int32Type); case "uint": case "uint32": return(UInt32Type); case "int64": return(Int64Type); case "uint64": return(UInt64Type); case "float": return(FloatType); case "double": return(DoubleType); case "string": return(StringType); } throw LoreException.Create(Visitor.Location).Describe($"Unable to resolve type: '{type}'"); }
/// <summary> /// Expect the specified token and string. /// </summary> /// <param name="token">Token.</param> /// <param name="strval">Strval.</param> public Lexeme <LoreToken> Expect(LoreToken token, string strval) { var current = Current; if (Accept(token, strval)) { return(current); } if (!See()) { throw LoreException.Create(Location).Describe("Unexpected end of file."); } var next = Read(); throw LoreException.Create(Location).Describe($"Unexpected token: '{next.Value}' ({next.Token})"); }
void CompileName(NameExpression expr) { // Check if the variable exists Symbol sym; if (!Table.FindSymbol(expr.Name, out sym)) { // The variable does not exist // Throw an exception throw LoreException.Create(Location).Describe($"Undefined variable: '{expr.Name}'"); } // TODO: Check if the variable was captured // Push the variable Stack.Push(Symbol.CreateAnonymous(sym.Value)); }
FloatExpression ParseFloat() { var expr = FloatExpression.Create(unit.Location); var literal = unit.Expect(LoreToken.FloatLiteral).Value; // Try parsing the string double floatval; try { floatval = double.Parse(literal, NumberStyles.Float, NumberFormatInfo.InvariantInfo); } catch (Exception e) { throw LoreException.Create(unit.Location).Describe($"Failed to parse float literal: ${e.Message}"); } // Set the value and return expr.SetValue(floatval); return(expr); }
/* * => [captures...] expression * or * => [captures...] { code... } * or * (parameters...) => [captures...] expression * or * (parameters...) => [captures...] { code... } */ LambdaExpression ParseLambda(TupleExpression argumentList = null) { var lambda = LambdaExpression.Create(unit.Location); // Parse parameter list if (argumentList == null || argumentList.Count == 0) { if (unit.Match(LoreToken.OpenParen)) { var parameters = ParseDeclarationArgumentList(); lambda.SetParameters(parameters); } } else { var lst = new List <NamedParameter> (); foreach (var argument in argumentList.Items) { var name = argument as NameExpression; if (name == null) { throw LoreException.Create(unit.Location).Describe($"Invalid parameter list in lambda declaration."); } lst.Add(NamedParameter.Create(name)); } lambda.SetParameters(lst); } // Expect arrow operator unit.Expect(LoreToken.Operator, "=>"); // Create master code block CodeBlock master = CodeBlock.Create(unit.Location); // Parse captures if (unit.Match(LoreToken.OpenBracket)) { // Parse captures unit.Expect(LoreToken.OpenBracket); while (!unit.Match(LoreToken.CloseBracket)) { var lex = unit.Read(); var capture = Capture.Create(unit, lex); master.AddCapture(capture); } unit.Expect(LoreToken.CloseBracket); } // Parse block if (unit.Match(LoreToken.OpenBrace)) { var block = ParseBlockWithoutCaptures(); // Merge captures master.Merge(block); } // Or parse expression else { // Create return statement for the result of the expression var retstmt = ReturnStatement.Create(unit.Location); retstmt.SetExpression(ParseExpression()); // Create a new block with the return statement var block = CodeBlock.Create(unit.Location); block.AddChild(retstmt); // Merge captures master.Merge(block); } // Set the lambda body lambda.SetBody(master); Console.WriteLine(lambda); return(lambda); }
void CompileFunction(FunctionDeclaration funcdecl) { // Check if a symbol with the name of the function is already defined Symbol sym = null; // Check if only local functions should be generated if (Table.TopScope.IsFunction) { // Declare the function DeclareFunctionPrototype(funcdecl); } if (!Table.FindSymbol(funcdecl.Name, out sym)) { throw LoreException.Create(funcdecl.Location).Describe($"Undefined function: '{funcdecl.Name}'."); } // Check if the symbol is a prototype if (!sym.IsPrototype) { throw new LoreException(funcdecl.Location).Describe($"Function without prototype: '{funcdecl.Name}'."); } // Get the function prototype LLVMValueRef functionRef = sym.Value; // Enter a new scope for the function Table.PushScope(Scope.CreateFunction(functionRef)); // Check if the function returns multiple values var returnType = LLVM.VoidType(); var tupleReturnTypes = new LLVMTypeRef [0]; if (funcdecl.ReturnsTuple) { var count = funcdecl.TupleReturnTypes.Count; tupleReturnTypes = new LLVMTypeRef [count]; for (var i = 0; i < count; i++) { var current = funcdecl.TupleReturnTypes [i]; tupleReturnTypes [i] = Helper.GetBuiltinTypeFromString(current.Name); } returnType = LLVM.StructType(tupleReturnTypes, true); } // Create the return type else if (funcdecl.HasReturnType) { returnType = Helper.GetBuiltinTypeFromString(funcdecl.ReturnType.Name); } // Add function parameters for (var i = 0; i < funcdecl.Parameters.Count; i++) { var argname = funcdecl.Parameters [i].Name.Name; // Get the i-th function parameter var param = LLVM.GetParam(functionRef, (uint)i); // Set the correct name LLVM.SetValueName(param, argname); // Add the named parameter to the scope Table.AddSymbol(Symbol.Create(argname, param, Location)); } // Create the entry block for the function var block = LLVM.AppendBasicBlock(functionRef, "entry"); LLVM.PositionBuilderAtEnd(Builder, block); // Visit the function body funcdecl.VisitChildren(this); // Return void and clear the stack if the // function return type is void if (Helper.CompareType(returnType, LLVM.VoidType())) { // Build return statement LLVM.BuildRetVoid(Builder); } else if (Stack.Count == 0) { // The function has a return type, // but nothing was returned! if (funcdecl.HasReturnType) { throw LoreException.Create(Location) .Describe($"Function '{funcdecl.Name}' has return type '{funcdecl.ReturnType.Name}' but does not return anything!") .Resolve($"Return a value of type '{funcdecl.ReturnType.Name}' or change the return type to 'void'."); } // There are no elements on the stack // Just return void LLVM.BuildRetVoid(Builder); } else if (funcdecl.ReturnsTuple && Stack.Count >= funcdecl.TupleReturnTypes.Count) { // Prepare element array var elems = new LLVMValueRef [funcdecl.TupleReturnTypes.Count]; // Fill the element array for (var i = 0; i < elems.Length; i++) { // Pop the element from the stack var current = Stack.Pop().Value; // Check if the current element is a defined symbol if (Table.FindSymbolByRef(current, out sym)) { // Check if the current element is a pointer if (sym.IsPointer) { // Load the element current = LLVM.BuildLoad(Builder, current, "tmpload"); } } // Cast the element to the right type current = Helper.BuildCast(Builder, current, tupleReturnTypes [i]); // Add the element to the array elems [elems.Length - i - 1] = current; } // Build the return statement LLVM.BuildAggregateRet(Builder, elems); // What the hellt } else if (funcdecl.HasReturnType && Stack.Count > 0) { // There are elements on the stack // Take the top element from the stack var elem = Stack.Pop().Value; // Check if the return element is a defined symbol if (Table.FindSymbolByRef(elem, out sym)) { // Check if the return element is a pointer if (sym.IsPointer) { // Load the element elem = LLVM.BuildLoad(Builder, elem, "tmpload"); } } Helper.BuildOptimalReturn(Builder, elem, returnType); } else { throw LoreException.Create(Location) .Describe($"Unable to construct a return value for function '{funcdecl.Name}'.") .Describe($"This is a compiler bug."); } // Validate the function if (LLVM.VerifyFunction(functionRef, LLVMVerifierFailureAction.LLVMPrintMessageAction).Value != 0) { throw LoreException.Create(funcdecl.Location).Describe($"Function failed to compile: '{funcdecl.Name}'"); } // Exit the scope of the function Table.PopScope(); if (Table.TopScope.IsFunction) { var func = Table.TopScope.Function; LLVM.PositionBuilderAtEnd(Builder, func.GetEntryBasicBlock()); } // Add the function symbol Table.AddSymbol(Symbol.Create(funcdecl.Name, functionRef, funcdecl.Location)); Stack.Clear(); }
internal void DeclareFunctionPrototype(FunctionDeclaration funcdecl) { // Prepare argument type array var argumentCount = funcdecl.Parameters.Count; var arguments = new LLVMTypeRef [Math.Max(argumentCount, 0)]; // Check if the function is already defined Symbol sym; if ((Table.TopScope.IsFunction && Table.TopScope.FindSymbol(funcdecl.Name, out sym)) || (!Table.TopScope.IsFunction && Table.FindSymbol(funcdecl.Name, out sym))) { var e = LoreException.Create(funcdecl.Location).Describe($"Redefinition of function '{funcdecl.Name}'!"); e.Describe($"Previous declaration was at {sym.Location}."); e.Resolve($"Rename the function to avoid a collision."); throw e; } // Add function parameter types to array for (var i = 0; i < funcdecl.Parameters.Count; i++) { arguments [i] = Helper.GetBuiltinTypeFromString(funcdecl.Parameters [i].Type.Name); } // Check if the function returns multiple values var returnType = LLVM.VoidType(); var tupleReturnTypes = new LLVMTypeRef [0]; if (funcdecl.ReturnsTuple) { var count = funcdecl.TupleReturnTypes.Count; tupleReturnTypes = new LLVMTypeRef [count]; for (var i = 0; i < count; i++) { var current = funcdecl.TupleReturnTypes [i]; tupleReturnTypes [i] = Helper.GetBuiltinTypeFromString(current.Name); } returnType = LLVM.StructType(tupleReturnTypes, true); } // Create the return type else if (funcdecl.HasReturnType) { returnType = Helper.GetBuiltinTypeFromString(funcdecl.ReturnType.Name); } // Create the function type var functionType = LLVM.FunctionType( ReturnType: returnType, ParamTypes: arguments, IsVarArg: LLVMFalse ); // Create the actual function var functionRef = LLVM.AddFunction(LLVMModule, funcdecl.Name, functionType); LLVM.SetLinkage(functionRef, LLVMLinkage.LLVMExternalLinkage); // Add the function prototype as a symbol Table.AddSymbol(Symbol.CreatePrototype(funcdecl.Name, functionRef, funcdecl.Location)); }
public LLVMValueRef BuildCast(LLVMBuilderRef builder, LLVMValueRef elem, LLVMTypeRef targetType) { // Check if the type of the element equals the targe type if (CompareType(elem, targetType)) { // There is no need to cast // Just return the element return(elem); } // Strings need special handling if (CompareType(targetType, StringType)) { LLVMValueRef indices; var gep = LLVM.BuildGEP(builder, elem, out indices, 0u, "tmpgep"); var ptr = LLVM.BuildPointerCast(builder, gep, StringType, "tmpptrcast"); return(ptr); } string strtype1, strtype2; switch (targetType.TypeKind) { case LLVMTypeKind.LLVMIntegerTypeKind: // Check if the integer fits if (IsInteger(elem)) { var lwidth = elem.TypeOf().GetIntTypeWidth(); var rwidth = targetType.GetIntTypeWidth(); if (lwidth > rwidth) { // The width of the result type is higher // than the width of the element type // Throw an exception strtype1 = elem.TypeOf().PrintTypeToString(); strtype2 = targetType.PrintTypeToString(); throw LoreException.Create(Visitor.Location) .Describe($"Unable to cast element of type '{strtype1}' to '{strtype2}':") .Describe($"The element is too big for the target integer type."); } } // Perform a float to signed integer cast if needed else if (IsFloatOrDouble(elem)) { elem = LLVM.BuildFPToSI(builder, elem, targetType, "tmpfptosicast"); } // Unsupported element type else { // Unable to perform a meaningful cast strtype1 = elem.TypeOf().PrintTypeToString(); strtype2 = targetType.PrintTypeToString(); throw LoreException.Create(Visitor.Location).Describe($"Unable to cast element of type '{strtype1}' to '{strtype2}':"); } // Perform an integer cast return(LLVM.BuildIntCast(builder, elem, targetType, "tmpcast")); case LLVMTypeKind.LLVMFloatTypeKind: case LLVMTypeKind.LLVMDoubleTypeKind: // Perform a signed integer to float cast if needed if (IsInteger(elem)) { elem = LLVM.BuildSIToFP(builder, elem, targetType, "tmpsitofpcast"); } return(LLVM.BuildFPCast(builder, elem, targetType, "tmpfcast")); } // Unable to perform a meaningful cast strtype1 = elem.TypeOf().PrintTypeToString(); strtype2 = targetType.PrintTypeToString(); throw LoreException.Create(Visitor.Location).Describe($"Unable to cast element of type '{strtype1}' to '{strtype2}':"); }
void CompileBinaryOperation(BinaryExpression expr) { // Compile the expression expr.VisitChildren(this); // Pop the right value from the stack var right = Stack.Pop().Value; var left = Stack.Pop().Value; LLVMValueRef result = LLVMNull; Func <LoreException> BuildUnsupportedBinaryOperationException = () => { var l = left.TypeOf().PrintTypeToString().Trim(); var r = right.TypeOf().PrintTypeToString().Trim(); return(LoreException.Create(Location) .Describe($"Invalid binary operation: {expr.Operation}") .Describe($"The operation is unsupported on types '{l}' and '{r}'")); }; if (expr.Operation == BinaryOperation.Assign) { // Try to find the symbol Symbol sym; if (!Table.FindSymbolByRef(left, out sym)) { // There is nothing to process throw BuildUnsupportedBinaryOperationException(); } // Check if the variable is mutable if (!sym.IsMutable) { // The variable is immutable throw BuildUnsupportedBinaryOperationException(); } // Check if the symbol is a pointer if (!sym.IsPointer) { // It is not possible to assign a new value to // the specified variable, since it is not a pointer // and is therefore not allocated on the stack. // Throw an exception throw LoreException.Create(Location).Describe($"Attempt to modify constant: '{sym.Name}"); } // Assign the new value to the variable var targetType = left.TypeOf().GetElementType(); right = Helper.BuildCast(Builder, right, targetType); LLVM.BuildStore(Builder, right, left); // Set the result result = right; } else if (Helper.IsInteger(left) && Helper.IsInteger(right)) { switch (expr.Operation) { case BinaryOperation.Add: result = LLVM.BuildAdd(Builder, left, right, "tmpadd"); break; case BinaryOperation.Subtract: result = LLVM.BuildSub(Builder, left, right, "tmpsub"); break; case BinaryOperation.Divide: result = LLVM.BuildUDiv(Builder, left, right, "tmpdiv"); break; case BinaryOperation.Multiply: result = LLVM.BuildMul(Builder, left, right, "tmpmul"); break; default: throw BuildUnsupportedBinaryOperationException(); } } else if ((Helper.IsFloatOrDouble(left) && Helper.IsInteger(right)) || (Helper.IsFloatOrDouble(right) && Helper.IsInteger(left)) || (Helper.IsFloatOrDouble(left) && Helper.IsFloatOrDouble(right))) { switch (expr.Operation) { case BinaryOperation.Add: result = LLVM.BuildFAdd(Builder, left, right, "tmpfadd"); break; case BinaryOperation.Subtract: result = LLVM.BuildFSub(Builder, left, right, "tmpfsub"); break; case BinaryOperation.Divide: result = LLVM.BuildFDiv(Builder, left, right, "tmpfdiv"); break; case BinaryOperation.Multiply: result = LLVM.BuildFMul(Builder, left, right, "tmpfmul"); break; default: throw BuildUnsupportedBinaryOperationException(); } } else { throw BuildUnsupportedBinaryOperationException(); } Stack.Push(Symbol.CreateAnonymous(result)); }
void CompileUnaryOperation(UnaryExpression expr) { // Compile the expression expr.VisitChildren(this); // Pop the right value from the stack var right = Stack.Pop().Value; LLVMValueRef result = LLVMNull; Func <LoreException> BuildUnsupportedUnaryOperationException = () => { var r = right.TypeOf().PrintTypeToString(); return(LoreException.Create(Location) .Describe($"Invalid unary operation: {expr.Operation}") .Describe($"The operation is unsupported on type '{r}'")); }; if (Helper.IsBoolean(right)) { if (expr.Operation == UnaryOperation.LogicalNot) { } else { throw BuildUnsupportedUnaryOperationException(); } } else if (Helper.IsFloatOrDouble(right)) { switch (expr.Operation) { case UnaryOperation.Negate: result = LLVM.BuildFNeg(Builder, right, "unop"); break; default: throw BuildUnsupportedUnaryOperationException(); } } else if (Helper.IsInteger(right)) { switch (expr.Operation) { case UnaryOperation.Negate: result = LLVM.BuildNeg(Builder, right, "unop"); break; case UnaryOperation.BitwiseNot: result = LLVM.BuildNot(Builder, right, "unop"); break; default: throw BuildUnsupportedUnaryOperationException(); } } else { throw BuildUnsupportedUnaryOperationException(); } Stack.Push(Symbol.CreateAnonymous(result)); }
AstNode ParseTerm() { // Try parsing a name if (unit.Match(LoreToken.Identifier)) { return(ParseName()); } // Try parsing an integer if (unit.Match(LoreToken.IntLiteral)) { return(ParseInteger()); } // Try parsing a float if (unit.Match(LoreToken.FloatLiteral)) { return(ParseFloat()); } // Try parsing a list if (unit.Match(LoreToken.OpenBracket)) { return(ParseList()); } // Try parsing a tuple, a lambda or an expression if (unit.Match(LoreToken.OpenParen)) { unit.Skip(); var istuple = false; // Parse the first expression var expr = ParseExpression(); // Create a temporary tuple expression // that holds the arguments of the lambda expression TupleExpression tmp = TupleExpression.Create(unit.Location); tmp.Add(expr); // Try parsing a tuple TupleExpression tpl = null; if (unit.Accept(LoreToken.Comma)) { tpl = ParseTuple(expr); istuple = true; } // Read the remaning parenthesis if // we did not successfully parse a tuple else { unit.Expect(LoreToken.CloseParen); } // Get the correct tuple tpl = tpl ?? tmp; // Try parsing a lambda expression if (unit.Match(LoreToken.Operator, "=>")) { return(ParseLambda(tpl)); } // Return the tuple if needed if (istuple) { return(tpl); } // This is neither a tuple nor a lambda // Just return the expression return(expr); } // Try parsing a keyword if (unit.Match(LoreToken.Keyword)) { // TODO: Implement true, false, null, etc switch (unit.Current.Value) { case "true": unit.Skip(); // TODO: Return TrueExpression return(null); case "false": unit.Skip(); // TODO: Return FalseExpression return(null); case "null": unit.Skip(); // TODO: Return NullExpression return(null); } } // Try parsing a string if (unit.Match(LoreToken.StringLiteral)) { return(ParseString()); } throw LoreException.Create(unit.Location).Describe("Unexpected end of term."); }