mirror of
https://github.com/iritmaximus/compilers.git
synced 2026-10-12 19:13:35 +03:00
Add simple block support + other small things including base64 encoding
This commit is contained in:
+1
-5
@@ -1,5 +1 @@
|
||||
.test-gadget
|
||||
.*_cache
|
||||
*.pyc
|
||||
__pycache__
|
||||
build
|
||||
target
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
__pycache__
|
||||
/.mypy_cache
|
||||
.test-gadget/
|
||||
test-gadget/
|
||||
test-gadget-client/
|
||||
build
|
||||
.cache
|
||||
target
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
target
|
||||
@@ -7,7 +7,9 @@ import java.net.{ServerSocket, Socket};
|
||||
import java.util.stream.Stream
|
||||
|
||||
import compiler.Tokenizer._
|
||||
import compiler.Parser._
|
||||
import compiler.Parser.{Unit => _, _} // Scala also has Unit
|
||||
import java.util.Base64
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
|
||||
@main def main(): Unit =
|
||||
@@ -17,6 +19,9 @@ import compiler.Parser._
|
||||
|
||||
def msg = "I was compiled by Scala 3. :)"
|
||||
|
||||
def base64Encode(source: String): String =
|
||||
return Base64.getEncoder.encodeToString(source.getBytes(StandardCharsets.UTF_8))
|
||||
|
||||
def run_server(): Unit =
|
||||
println("Starting server...")
|
||||
val serverSocket: ServerSocket = new ServerSocket(3000);
|
||||
@@ -40,7 +45,7 @@ def run_server(): Unit =
|
||||
|
||||
val response = command match {
|
||||
case "ping" => "{}"
|
||||
case "compile" => s"{\"program\": \"${compile(json("code").str)}\"}"
|
||||
case "compile" => s"{\"program\": \"${base64Encode(compile(json("code").str))}\"}"
|
||||
case _ => s"{\"error\": \"Incorrect json request body\"}"
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,12 @@ import compiler.Tokenizer.{Token, TokenType}
|
||||
sealed abstract class Expression:
|
||||
override def toString() = s"Expression(TODO)"
|
||||
|
||||
// Return value of statement without return value
|
||||
case class Unit() {
|
||||
override def toString: String = "Unit()"
|
||||
}
|
||||
|
||||
case class Literal(value: Int | Boolean) extends Expression {
|
||||
case class Literal(value: Int | Unit) extends Expression {
|
||||
override def toString: String = s"Literal($value)"
|
||||
}
|
||||
case class Identifier(name: String) extends Expression {
|
||||
@@ -32,6 +36,9 @@ case class Declaration(name: Identifier, body: Expression) extends Expression {
|
||||
case class Function(name: String, arguments: List[Expression]) extends Expression {
|
||||
override def toString: String = s"Function($name(${arguments.mkString(", ")}))"
|
||||
}
|
||||
case class Block(expressions: List[Expression]) extends Expression {
|
||||
override def toString: String = s"Block({\n${expressions.mkString(";\n")}\n})"
|
||||
}
|
||||
case class Other() extends Expression {
|
||||
override def toString: String = s"Other()"
|
||||
}
|
||||
@@ -121,6 +128,7 @@ class Parser(tokens: List[Token]):
|
||||
case TokenType.Identifier => parseIdentifier()
|
||||
case TokenType.Operator if token.value == "-" => parseUnaryOperator()
|
||||
case TokenType.Punctuation if token.value == "(" => parseParenthisized()
|
||||
case TokenType.Punctuation if token.value == "{" => parseBlock()
|
||||
case _ => throw new Exception(s"Incorrect token type: Expected (, int literal or identifier, got ${token.tokenType} token")
|
||||
}
|
||||
|
||||
@@ -182,6 +190,28 @@ class Parser(tokens: List[Token]):
|
||||
return Declaration(name, body)
|
||||
|
||||
|
||||
def parseBlock(): Expression =
|
||||
consume(Some("{")).get
|
||||
var expressions: List[Expression] = List()
|
||||
var hasSemicolon: Boolean = false
|
||||
|
||||
println(s"Peek value: ${peek().value}")
|
||||
while
|
||||
peek().value != "}"
|
||||
do
|
||||
hasSemicolon = false
|
||||
expressions = expressions ::: List(parseExpression())
|
||||
println(s"Expressions $expressions")
|
||||
if peek().value != "}" then
|
||||
consume(Some(";")).get
|
||||
hasSemicolon = true
|
||||
|
||||
if hasSemicolon then
|
||||
expressions = expressions ::: List(Literal(Unit()))
|
||||
|
||||
consume(Some("}")).get
|
||||
return Block(expressions)
|
||||
|
||||
def parseExpression(initialDepth: Int = 0): Expression =
|
||||
var depth = initialDepth
|
||||
var left = parseFactor()
|
||||
@@ -212,4 +242,5 @@ class Parser(tokens: List[Token]):
|
||||
object Parser:
|
||||
def parse(tokens: List[Token]): Try[Expression] =
|
||||
val parser = Parser(tokens)
|
||||
return Success(Other())
|
||||
val result = parser.parseExpression()
|
||||
return Success(result)
|
||||
|
||||
@@ -229,6 +229,21 @@ class ParserIfThenElseTests extends BaseParserTests {
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse nested if else") {
|
||||
val p = testParser("if a then b else if c then d else e")
|
||||
val result = p.parseExpression()
|
||||
val expected = IfThenElse(
|
||||
Identifier("a"),
|
||||
Identifier("b"),
|
||||
Some(IfThenElse(
|
||||
Identifier("c"),
|
||||
Identifier("d"),
|
||||
Some(Identifier("e"))
|
||||
))
|
||||
)
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should not parse if then with extra identifiers") {
|
||||
val token = Tokenizer.tokenize("if true while").get(2)
|
||||
val p = testParser("if true while then 1 + 1")
|
||||
@@ -623,3 +638,63 @@ class ParserDeclarationTests extends BaseParserTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ParserBlockTests extends BaseParserTests {
|
||||
test("should parse empty block") {
|
||||
val p = testParser("{}")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List())
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse simple block with single expression (no semicolon)") {
|
||||
val p = testParser("{ x }")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List(Identifier("x")))
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse simple block with single expression (semicolon)") {
|
||||
val p = testParser("{ x; }")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List(Identifier("x"), Literal(Unit())))
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse block with single declaration (no semicolon)") {
|
||||
val p = testParser("{ var x = 1 }")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List(Declaration(Identifier("x"), Literal(1))))
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse block with sigle declaration (semicolon)") {
|
||||
val p = testParser("{ var x = 1; }")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List(Declaration(Identifier("x"), Literal(1)), Literal(Unit())))
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse block with multiple declarations") {
|
||||
val p = testParser("{ var x = 1; var y = 2; }")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List(
|
||||
Declaration(Identifier("x"), Literal(1)),
|
||||
Declaration(Identifier("y"), Literal(2)),
|
||||
Literal(Unit()
|
||||
)))
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
test("should parse with multiple function calls and return value") {
|
||||
val p = testParser("{ f(a); x = y; f(x) }")
|
||||
val result = p.parseExpression()
|
||||
val expected = Block(List(
|
||||
Function("f", List(Identifier("a"))),
|
||||
BinaryOperator(Identifier("x"), "=", Identifier("y")),
|
||||
Function("f", List(Identifier("x")))
|
||||
))
|
||||
|
||||
assertEquals(result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
echo '{"command": "compile", "code": "1+1 == 2"}' | netcat localhost 3000
|
||||
echo '{"command": "compile", "code": "1+1 == 2"}' | nc localhost 3000
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
test-gadget-client/target/release/test-gadget-client
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Runs the correct test-gadget client program in .test-gadget/test-gadget-client-$PLATFORM
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_platform_binary() -> str:
|
||||
system = platform.system().lower()
|
||||
if system == "darwin":
|
||||
return "test-gadget-client-macos"
|
||||
elif system == "windows":
|
||||
return "test-gadget-client-windows.exe"
|
||||
elif system == "linux":
|
||||
return "test-gadget-client-linux"
|
||||
else:
|
||||
print(f"Unsupported platform: {system}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
script_dir = Path(__file__).parent
|
||||
dist_dir = script_dir / ".test-gadget"
|
||||
binary = dist_dir / get_platform_binary()
|
||||
|
||||
if not binary.exists():
|
||||
print(f"Program not found: {binary}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
os.execv(str(binary), [str(binary)] + sys.argv[1:])
|
||||
Reference in New Issue
Block a user