mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
String literal support.
This commit is contained in:
parent
c418694307
commit
ddde99f38d
@ -49,4 +49,5 @@ results = [
|
||||
notEqual(5, 6)
|
||||
]
|
||||
|
||||
println("results:")
|
||||
println(results)
|
||||
|
@ -12,6 +12,7 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
override fun visitIntLiteral(node: IntLiteral) = handler(node)
|
||||
override fun visitBooleanLiteral(node: BooleanLiteral) = handler(node)
|
||||
override fun visitListLiteral(node: ListLiteral) = handler(node)
|
||||
override fun visitStringLiteral(node: StringLiteral) = handler(node)
|
||||
override fun visitParentheses(node: Parentheses) = handler(node)
|
||||
override fun visitPrefixOperation(node: PrefixOperation) = handler(node)
|
||||
override fun visitInfixOperation(node: InfixOperation) = handler(node)
|
||||
|
@ -1,20 +1,19 @@
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import gay.pizza.pork.ast.NodeTypeTrait.*
|
||||
|
||||
enum class NodeType(val parent: NodeType? = null, vararg traits: NodeTypeTrait) {
|
||||
enum class NodeType(val parent: NodeType? = null) {
|
||||
Node,
|
||||
Symbol(Node),
|
||||
Expression(Node, Intermediate),
|
||||
Expression(Node),
|
||||
Program(Node),
|
||||
IntLiteral(Expression, Literal),
|
||||
BooleanLiteral(Expression, Literal),
|
||||
ListLiteral(Expression, Literal),
|
||||
IntLiteral(Expression),
|
||||
BooleanLiteral(Expression),
|
||||
ListLiteral(Expression),
|
||||
StringLiteral(Expression),
|
||||
Parentheses(Expression),
|
||||
Define(Expression),
|
||||
Lambda(Expression),
|
||||
PrefixOperation(Expression, Operation),
|
||||
InfixOperation(Expression, Operation),
|
||||
PrefixOperation(Expression),
|
||||
InfixOperation(Expression),
|
||||
SymbolReference(Expression),
|
||||
FunctionCall(Expression),
|
||||
If(Expression);
|
||||
|
@ -13,6 +13,7 @@ interface NodeVisitor<T> {
|
||||
fun visitIntLiteral(node: IntLiteral): T
|
||||
fun visitBooleanLiteral(node: BooleanLiteral): T
|
||||
fun visitListLiteral(node: ListLiteral): T
|
||||
fun visitStringLiteral(node: StringLiteral): T
|
||||
|
||||
fun visitParentheses(node: Parentheses): T
|
||||
fun visitPrefixOperation(node: PrefixOperation): T
|
||||
@ -24,6 +25,7 @@ interface NodeVisitor<T> {
|
||||
is IntLiteral -> visitIntLiteral(node)
|
||||
is BooleanLiteral -> visitBooleanLiteral(node)
|
||||
is ListLiteral -> visitListLiteral(node)
|
||||
is StringLiteral -> visitStringLiteral(node)
|
||||
is Parentheses -> visitParentheses(node)
|
||||
is InfixOperation -> visitInfixOperation(node)
|
||||
is PrefixOperation -> visitPrefixOperation(node)
|
||||
@ -32,14 +34,12 @@ interface NodeVisitor<T> {
|
||||
is FunctionCall -> visitFunctionCall(node)
|
||||
is SymbolReference -> visitReference(node)
|
||||
is If -> visitIf(node)
|
||||
else -> throw RuntimeException("Unknown Expression")
|
||||
}
|
||||
|
||||
fun visit(node: Node): T = when (node) {
|
||||
is Expression -> visitExpression(node)
|
||||
is Symbol -> visitSymbol(node)
|
||||
is Program -> visitProgram(node)
|
||||
else -> throw RuntimeException("Unknown Node")
|
||||
}
|
||||
|
||||
fun visitNodes(vararg nodes: Node?): List<T> =
|
||||
|
@ -1,6 +1,7 @@
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import gay.pizza.pork.ast.nodes.*
|
||||
import gay.pizza.pork.util.StringEscape
|
||||
|
||||
class Printer(private val buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
private var indent = 0
|
||||
@ -109,6 +110,12 @@ class Printer(private val buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
append("]")
|
||||
}
|
||||
|
||||
override fun visitStringLiteral(node: StringLiteral) {
|
||||
append("\"")
|
||||
append(StringEscape.escape(node.text))
|
||||
append("\"")
|
||||
}
|
||||
|
||||
override fun visitParentheses(node: Parentheses) {
|
||||
append("(")
|
||||
visit(node.expression)
|
||||
|
18
src/main/kotlin/gay/pizza/pork/ast/nodes/StringLiteral.kt
Normal file
18
src/main/kotlin/gay/pizza/pork/ast/nodes/StringLiteral.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package gay.pizza.pork.ast.nodes
|
||||
|
||||
import gay.pizza.pork.ast.NodeType
|
||||
|
||||
class StringLiteral(val text: String) : Expression() {
|
||||
override val type: NodeType = NodeType.StringLiteral
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is StringLiteral) return false
|
||||
return other.text == text
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = text.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.*
|
||||
import gay.pizza.pork.ast.nodes.*
|
||||
import gay.pizza.pork.util.StringEscape
|
||||
|
||||
class KotlinCompiler : NodeVisitor<String> {
|
||||
override fun visitDefine(node: Define): String =
|
||||
@ -55,6 +56,9 @@ class KotlinCompiler : NodeVisitor<String> {
|
||||
append(")")
|
||||
}
|
||||
|
||||
override fun visitStringLiteral(node: StringLiteral): String =
|
||||
"\"" + StringEscape.escape(node.text) + "\""
|
||||
|
||||
override fun visitParentheses(node: Parentheses): String =
|
||||
"(${visit(node.expression)})"
|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
package gay.pizza.pork.eval
|
||||
|
||||
class Arguments(val values: List<Any>) {
|
||||
companion object {
|
||||
val Zero = Arguments(emptyList())
|
||||
}
|
||||
}
|
||||
class Arguments(val values: List<Any>)
|
||||
|
@ -58,6 +58,7 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
|
||||
override fun visitIntLiteral(node: IntLiteral): Any = node.value
|
||||
override fun visitBooleanLiteral(node: BooleanLiteral): Any = node.value
|
||||
override fun visitListLiteral(node: ListLiteral): Any = node.items.map { visit(it) }
|
||||
override fun visitStringLiteral(node: StringLiteral): Any = node.text
|
||||
|
||||
override fun visitParentheses(node: Parentheses): Any = visit(node.expression)
|
||||
|
||||
|
@ -3,6 +3,7 @@ package gay.pizza.pork.parse
|
||||
open class AnsiHighlightScheme : HighlightScheme {
|
||||
override fun highlight(token: Token): Highlight {
|
||||
val attributes = when (token.type.family) {
|
||||
TokenFamily.StringLiteralFamily -> string()
|
||||
TokenFamily.OperatorFamily -> operator()
|
||||
TokenFamily.KeywordFamily -> keyword()
|
||||
TokenFamily.SymbolFamily -> symbol()
|
||||
@ -15,8 +16,9 @@ open class AnsiHighlightScheme : HighlightScheme {
|
||||
} else Highlight(token)
|
||||
}
|
||||
|
||||
open fun operator(): AnsiAttributes = AnsiAttributes("32m")
|
||||
open fun string(): AnsiAttributes = AnsiAttributes("32m")
|
||||
open fun symbol(): AnsiAttributes = AnsiAttributes("33m")
|
||||
open fun operator(): AnsiAttributes = AnsiAttributes("34m")
|
||||
open fun keyword(): AnsiAttributes = AnsiAttributes("35m")
|
||||
open fun comment(): AnsiAttributes = AnsiAttributes("37m")
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package gay.pizza.pork.parse
|
||||
|
||||
import gay.pizza.pork.ast.nodes.*
|
||||
import gay.pizza.pork.util.StringEscape
|
||||
|
||||
class Parser(source: PeekableSource<Token>) {
|
||||
private val unsanitizedSource = source
|
||||
@ -65,6 +66,11 @@ class Parser(source: PeekableSource<Token>) {
|
||||
fun readExpression(): Expression {
|
||||
val token = peek()
|
||||
val expression = when (token.type) {
|
||||
TokenType.StringLiteral -> {
|
||||
expect(TokenType.StringLiteral)
|
||||
return StringLiteral(StringEscape.unescape(StringEscape.unquote(token.text)))
|
||||
}
|
||||
|
||||
TokenType.IntLiteral -> {
|
||||
readIntLiteral()
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ enum class TokenFamily : TokenTypeProperty {
|
||||
KeywordFamily,
|
||||
SymbolFamily,
|
||||
NumericLiteralFamily,
|
||||
StringLiteralFamily,
|
||||
CommentFamily,
|
||||
OtherFamily
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import gay.pizza.pork.parse.TokenFamily.*
|
||||
enum class TokenType(vararg properties: TokenTypeProperty) {
|
||||
Symbol(SymbolFamily),
|
||||
IntLiteral(NumericLiteralFamily),
|
||||
StringLiteral(StringLiteralFamily),
|
||||
Equality(OperatorFamily),
|
||||
Inequality(OperatorFamily),
|
||||
Equals(SingleChar('='), Promotion('=', Equality)),
|
||||
|
@ -89,6 +89,23 @@ class Tokenizer(val source: CharSource) {
|
||||
return Token(TokenType.LineComment, tokenStart, comment)
|
||||
}
|
||||
|
||||
private fun readStringLiteral(firstChar: Char): Token {
|
||||
val string = buildString {
|
||||
append(firstChar)
|
||||
while (true) {
|
||||
val char = source.peek()
|
||||
if (char == CharSource.NullChar) {
|
||||
throw RuntimeException("Unterminated string.")
|
||||
}
|
||||
append(source.next())
|
||||
if (char == '"') {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return Token(TokenType.StringLiteral, tokenStart, string)
|
||||
}
|
||||
|
||||
fun next(): Token {
|
||||
while (source.peek() != CharSource.NullChar) {
|
||||
tokenStart = source.currentIndex
|
||||
@ -132,6 +149,11 @@ class Tokenizer(val source: CharSource) {
|
||||
if (isSymbol(char)) {
|
||||
return readSymbolOrKeyword(char)
|
||||
}
|
||||
|
||||
if (char == '"') {
|
||||
return readStringLiteral(char)
|
||||
}
|
||||
|
||||
throw RuntimeException("Failed to parse: (${char}) next ${source.peek()}")
|
||||
}
|
||||
return Token.endOfFile(source.currentIndex)
|
||||
|
8
src/main/kotlin/gay/pizza/pork/util/StringEscape.kt
Normal file
8
src/main/kotlin/gay/pizza/pork/util/StringEscape.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package gay.pizza.pork.util
|
||||
|
||||
object StringEscape {
|
||||
fun escape(input: String): String = input.replace("\n", "\\n")
|
||||
fun unescape(input: String): String = input.replace("\\n", "\n")
|
||||
|
||||
fun unquote(input: String): String = input.substring(1, input.length - 1)
|
||||
}
|
Loading…
Reference in New Issue
Block a user