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